Commit 9db0a1d0 authored by vasilii's avatar vasilii Committed by Commit bot
parent 2fe091b1
...@@ -48,6 +48,7 @@ class MockPasswordStore : public PasswordStore { ...@@ -48,6 +48,7 @@ class MockPasswordStore : public PasswordStore {
bool(std::vector<autofill::PasswordForm*>*)); bool(std::vector<autofill::PasswordForm*>*));
MOCK_METHOD1(FillBlacklistLogins, MOCK_METHOD1(FillBlacklistLogins,
bool(std::vector<autofill::PasswordForm*>*)); bool(std::vector<autofill::PasswordForm*>*));
MOCK_METHOD1(NotifyLoginsChanged, void(const PasswordStoreChangeList&));
PasswordStoreSync* GetSyncInterface() { return this; } PasswordStoreSync* GetSyncInterface() { return this; }
......
...@@ -16,24 +16,26 @@ ...@@ -16,24 +16,26 @@
namespace password_manager { namespace password_manager {
namespace { // Converts the |password| into a SyncData object.
syncer::SyncData SyncDataFromPassword(const autofill::PasswordForm& password);
// Describes the result of merging sync and local passwords. // Extracts the |PasswordForm| data from sync's protobuf format.
enum MergeResult { autofill::PasswordForm PasswordFromSpecifics(
IDENTICAL, const sync_pb::PasswordSpecificsData& password);
SYNC,
LOCAL, // Returns the unique tag that will serve as the sync identifier for the
}; // |password| entry.
std::string MakePasswordSyncTag(const sync_pb::PasswordSpecificsData& password);
std::string MakePasswordSyncTag(const autofill::PasswordForm& password);
namespace {
// Merges the local and sync passwords and outputs the entry into // Returns true iff |password_specifics| and |password_specifics| are equal
// |new_password_form|. Returns MergeResult value describing the content of // memberwise.
// |new_password_form|. bool AreLocalAndSyncPasswordsEqual(
MergeResult MergeLocalAndSyncPasswords(
const sync_pb::PasswordSpecificsData& password_specifics, const sync_pb::PasswordSpecificsData& password_specifics,
const autofill::PasswordForm& password_form, const autofill::PasswordForm& password_form) {
autofill::PasswordForm* new_password_form) { return (password_form.scheme == password_specifics.scheme() &&
DCHECK(new_password_form);
if (password_form.scheme == password_specifics.scheme() &&
password_form.signon_realm == password_specifics.signon_realm() && password_form.signon_realm == password_specifics.signon_realm() &&
password_form.origin.spec() == password_specifics.origin() && password_form.origin.spec() == password_specifics.origin() &&
password_form.action.spec() == password_specifics.action() && password_form.action.spec() == password_specifics.action() &&
...@@ -49,66 +51,61 @@ MergeResult MergeLocalAndSyncPasswords( ...@@ -49,66 +51,61 @@ MergeResult MergeLocalAndSyncPasswords(
password_form.preferred == password_specifics.preferred() && password_form.preferred == password_specifics.preferred() &&
password_form.date_created.ToInternalValue() == password_form.date_created.ToInternalValue() ==
password_specifics.date_created() && password_specifics.date_created() &&
password_form.blacklisted_by_user == password_specifics.blacklisted() && password_form.blacklisted_by_user ==
password_specifics.blacklisted() &&
password_form.type == password_specifics.type() && password_form.type == password_specifics.type() &&
password_form.times_used == password_specifics.times_used()) { password_form.times_used == password_specifics.times_used());
return IDENTICAL;
}
// If the passwords differ, take the one that was created more recently.
if (base::Time::FromInternalValue(password_specifics.date_created()) <
password_form.date_created) {
*new_password_form = password_form;
return LOCAL;
}
PasswordFromSpecifics(password_specifics, new_password_form);
return SYNC;
}
std::string MakePasswordSyncTag(const std::string& origin_url,
const std::string& username_element,
const std::string& username_value,
const std::string& password_element,
const std::string& signon_realm) {
return net::EscapePath(origin_url) + "|" +
net::EscapePath(username_element) + "|" +
net::EscapePath(username_value) + "|" +
net::EscapePath(password_element) + "|" +
net::EscapePath(signon_realm);
}
std::string MakePasswordSyncTag(const autofill::PasswordForm& password) {
return MakePasswordSyncTag(password.origin.spec(),
base::UTF16ToUTF8(password.username_element),
base::UTF16ToUTF8(password.username_value),
base::UTF16ToUTF8(password.password_element),
password.signon_realm);
} }
syncer::SyncChange::SyncChangeType GetSyncChangeType( syncer::SyncChange::SyncChangeType GetSyncChangeType(
PasswordStoreChange::Type type) { PasswordStoreChange::Type type) {
switch (type) { switch (type) {
case PasswordStoreChange::ADD: case PasswordStoreChange::ADD: return syncer::SyncChange::ACTION_ADD;
return syncer::SyncChange::ACTION_ADD; case PasswordStoreChange::UPDATE: return syncer::SyncChange::ACTION_UPDATE;
case PasswordStoreChange::UPDATE: case PasswordStoreChange::REMOVE: return syncer::SyncChange::ACTION_DELETE;
return syncer::SyncChange::ACTION_UPDATE;
case PasswordStoreChange::REMOVE:
return syncer::SyncChange::ACTION_DELETE;
} }
NOTREACHED(); NOTREACHED();
return syncer::SyncChange::ACTION_INVALID; return syncer::SyncChange::ACTION_INVALID;
} }
void AppendChanges(const PasswordStoreChangeList& new_changes, // Creates a PasswordForm from |specifics| and |sync_time|, appends it to
PasswordStoreChangeList* all_changes) { // |entries|.
all_changes->insert(all_changes->end(), void AppendPasswordFromSpecifics(
new_changes.begin(), const sync_pb::PasswordSpecificsData& specifics,
new_changes.end()); base::Time sync_time,
ScopedVector<autofill::PasswordForm>* entries) {
entries->push_back(
new autofill::PasswordForm(PasswordFromSpecifics(specifics)));
entries->back()->date_synced = sync_time;
} }
} // namespace } // namespace
struct PasswordSyncableService::SyncEntries {
ScopedVector<autofill::PasswordForm>* EntriesForChangeType(
syncer::SyncChange::SyncChangeType type) {
switch (type) {
case syncer::SyncChange::ACTION_ADD: return &new_entries;
case syncer::SyncChange::ACTION_UPDATE: return &updated_entries;
case syncer::SyncChange::ACTION_DELETE: return &deleted_entries;
case syncer::SyncChange::ACTION_INVALID: return NULL;
}
NOTREACHED();
return NULL;
}
// List that contains the entries that are known only to sync.
ScopedVector<autofill::PasswordForm> new_entries;
// List that contains the entries that are known to both sync and the local
// database but have updates in sync. They need to be updated in the local
// database.
ScopedVector<autofill::PasswordForm> updated_entries;
// The list of entries to be deleted from the local database.
ScopedVector<autofill::PasswordForm> deleted_entries;
};
PasswordSyncableService::PasswordSyncableService( PasswordSyncableService::PasswordSyncableService(
PasswordStoreSync* password_store) PasswordStoreSync* password_store)
: password_store_(password_store), : password_store_(password_store),
...@@ -151,13 +148,7 @@ syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing( ...@@ -151,13 +148,7 @@ syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing(
merge_result.set_num_items_before_association(new_local_entries.size()); merge_result.set_num_items_before_association(new_local_entries.size());
// List that contains the entries that are known only to sync. SyncEntries sync_entries;
ScopedVector<autofill::PasswordForm> new_sync_entries;
// List that contains the entries that are known to both sync and db but
// have updates in sync. They need to be updated in the passwords db.
ScopedVector<autofill::PasswordForm> updated_sync_entries;
// Changes from password db that need to be propagated to sync. // Changes from password db that need to be propagated to sync.
syncer::SyncChangeList updated_db_entries; syncer::SyncChangeList updated_db_entries;
for (syncer::SyncDataList::const_iterator sync_iter = for (syncer::SyncDataList::const_iterator sync_iter =
...@@ -165,25 +156,22 @@ syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing( ...@@ -165,25 +156,22 @@ syncer::SyncMergeResult PasswordSyncableService::MergeDataAndStartSyncing(
sync_iter != initial_sync_data.end(); ++sync_iter) { sync_iter != initial_sync_data.end(); ++sync_iter) {
CreateOrUpdateEntry(*sync_iter, CreateOrUpdateEntry(*sync_iter,
&new_local_entries, &new_local_entries,
&new_sync_entries, &sync_entries,
&updated_sync_entries,
&updated_db_entries); &updated_db_entries);
} }
WriteToPasswordStore(new_sync_entries.get(), WriteToPasswordStore(sync_entries);
updated_sync_entries.get(),
PasswordForms());
merge_result.set_num_items_after_association( merge_result.set_num_items_after_association(
merge_result.num_items_before_association() + new_sync_entries.size()); merge_result.num_items_before_association() +
sync_entries.new_entries.size());
merge_result.set_num_items_added(new_sync_entries.size()); merge_result.set_num_items_added(sync_entries.new_entries.size());
merge_result.set_num_items_modified(updated_sync_entries.size()); merge_result.set_num_items_modified(sync_entries.updated_entries.size());
for (PasswordEntryMap::iterator it = new_local_entries.begin(); for (PasswordEntryMap::iterator it = new_local_entries.begin();
it != new_local_entries.end(); it != new_local_entries.end(); ++it) {
++it) {
updated_db_entries.push_back( updated_db_entries.push_back(
syncer::SyncChange(FROM_HERE, syncer::SyncChange(FROM_HERE,
syncer::SyncChange::ACTION_ADD, syncer::SyncChange::ACTION_ADD,
...@@ -223,45 +211,23 @@ syncer::SyncError PasswordSyncableService::ProcessSyncChanges( ...@@ -223,45 +211,23 @@ syncer::SyncError PasswordSyncableService::ProcessSyncChanges(
const syncer::SyncChangeList& change_list) { const syncer::SyncChangeList& change_list) {
DCHECK(CalledOnValidThread()); DCHECK(CalledOnValidThread());
base::AutoReset<bool> processing_changes(&is_processing_sync_changes_, true); base::AutoReset<bool> processing_changes(&is_processing_sync_changes_, true);
// The |db_entries_map| and associated vectors are filled only in case of SyncEntries sync_entries;
// update change.
ScopedVector<autofill::PasswordForm> new_sync_entries;
ScopedVector<autofill::PasswordForm> updated_sync_entries;
ScopedVector<autofill::PasswordForm> deleted_entries;
base::Time time_now = base::Time::Now(); base::Time time_now = base::Time::Now();
for (syncer::SyncChangeList::const_iterator it = change_list.begin(); for (syncer::SyncChangeList::const_iterator it = change_list.begin();
it != change_list.end(); it != change_list.end(); ++it) {
++it) {
const sync_pb::EntitySpecifics& specifics = it->sync_data().GetSpecifics(); const sync_pb::EntitySpecifics& specifics = it->sync_data().GetSpecifics();
scoped_ptr<autofill::PasswordForm> form(new autofill::PasswordForm); ScopedVector<autofill::PasswordForm>* entries =
PasswordFromSpecifics(specifics.password().client_only_encrypted_data(), sync_entries.EntriesForChangeType(it->change_type());
form.get()); if (!entries) {
switch (it->change_type()) {
case syncer::SyncChange::ACTION_ADD: {
form->date_synced = time_now;
new_sync_entries.push_back(form.release());
break;
}
case syncer::SyncChange::ACTION_UPDATE: {
form->date_synced = time_now;
updated_sync_entries.push_back(form.release());
break;
}
case syncer::SyncChange::ACTION_DELETE: {
deleted_entries.push_back(form.release());
break;
}
case syncer::SyncChange::ACTION_INVALID:
return sync_error_factory_->CreateAndUploadError( return sync_error_factory_->CreateAndUploadError(
FROM_HERE, FROM_HERE,
"Failed to process sync changes for passwords datatype."); "Failed to process sync changes for passwords datatype.");
} }
AppendPasswordFromSpecifics(
specifics.password().client_only_encrypted_data(), time_now, entries);
} }
WriteToPasswordStore(new_sync_entries.get(), WriteToPasswordStore(sync_entries);
updated_sync_entries.get(),
deleted_entries.get());
return syncer::SyncError(); return syncer::SyncError();
} }
...@@ -283,8 +249,7 @@ void PasswordSyncableService::ActOnPasswordStoreChanges( ...@@ -283,8 +249,7 @@ void PasswordSyncableService::ActOnPasswordStoreChanges(
return; return;
syncer::SyncChangeList sync_changes; syncer::SyncChangeList sync_changes;
for (PasswordStoreChangeList::const_iterator it = local_changes.begin(); for (PasswordStoreChangeList::const_iterator it = local_changes.begin();
it != local_changes.end(); it != local_changes.end(); ++it) {
++it) {
syncer::SyncData data = (it->type() == PasswordStoreChange::REMOVE ? syncer::SyncData data = (it->type() == PasswordStoreChange::REMOVE ?
syncer::SyncData::CreateLocalDelete(MakePasswordSyncTag(it->form()), syncer::SyncData::CreateLocalDelete(MakePasswordSyncTag(it->form()),
syncer::PASSWORDS) : syncer::PASSWORDS) :
...@@ -318,57 +283,37 @@ bool PasswordSyncableService::ReadFromPasswordStore( ...@@ -318,57 +283,37 @@ bool PasswordSyncableService::ReadFromPasswordStore(
if (!passwords_entry_map) if (!passwords_entry_map)
return true; return true;
PasswordEntryMap& entry_map = *passwords_entry_map;
for (PasswordForms::iterator it = password_entries->begin(); for (PasswordForms::iterator it = password_entries->begin();
it != password_entries->end(); ++it) { it != password_entries->end(); ++it) {
autofill::PasswordForm* password_form = *it; autofill::PasswordForm* password_form = *it;
passwords_entry_map->insert( entry_map[MakePasswordSyncTag(*password_form)] = password_form;
std::make_pair(MakePasswordSyncTag(*password_form), password_form));
} }
return true; return true;
} }
void PasswordSyncableService::WriteToPasswordStore( void PasswordSyncableService::WriteToPasswordStore(const SyncEntries& entries) {
const PasswordForms& new_entries,
const PasswordForms& updated_entries,
const PasswordForms& deleted_entries) {
PasswordStoreChangeList changes; PasswordStoreChangeList changes;
for (std::vector<autofill::PasswordForm*>::const_iterator it = WriteEntriesToDatabase(&PasswordStoreSync::AddLoginImpl,
new_entries.begin(); entries.new_entries.get(),
it != new_entries.end(); &changes);
++it) { WriteEntriesToDatabase(&PasswordStoreSync::UpdateLoginImpl,
AppendChanges(password_store_->AddLoginImpl(**it), &changes); entries.updated_entries.get(),
} &changes);
WriteEntriesToDatabase(&PasswordStoreSync::RemoveLoginImpl,
for (std::vector<autofill::PasswordForm*>::const_iterator it = entries.deleted_entries.get(),
updated_entries.begin(); &changes);
it != updated_entries.end();
++it) {
AppendChanges(password_store_->UpdateLoginImpl(**it), &changes);
}
for (std::vector<autofill::PasswordForm*>::const_iterator it =
deleted_entries.begin();
it != deleted_entries.end();
++it) {
AppendChanges(password_store_->RemoveLoginImpl(**it), &changes);
}
// We have to notify password store observers of the change by hand since // We have to notify password store observers of the change by hand since
// we use internal password store interfaces to make changes synchronously. // we use internal password store interfaces to make changes synchronously.
NotifyPasswordStoreOfLoginChanges(changes);
}
void PasswordSyncableService::NotifyPasswordStoreOfLoginChanges(
const PasswordStoreChangeList& changes) {
password_store_->NotifyLoginsChanged(changes); password_store_->NotifyLoginsChanged(changes);
} }
void PasswordSyncableService::CreateOrUpdateEntry( void PasswordSyncableService::CreateOrUpdateEntry(
const syncer::SyncData& data, const syncer::SyncData& data,
PasswordEntryMap* umatched_data_from_password_db, PasswordEntryMap* unmatched_data_from_password_db,
ScopedVector<autofill::PasswordForm>* new_sync_entries, SyncEntries* sync_entries,
ScopedVector<autofill::PasswordForm>* updated_sync_entries,
syncer::SyncChangeList* updated_db_entries) { syncer::SyncChangeList* updated_db_entries) {
const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
const sync_pb::PasswordSpecificsData& password_specifics( const sync_pb::PasswordSpecificsData& password_specifics(
...@@ -377,39 +322,48 @@ void PasswordSyncableService::CreateOrUpdateEntry( ...@@ -377,39 +322,48 @@ void PasswordSyncableService::CreateOrUpdateEntry(
// Check whether the data from sync is already in the password store. // Check whether the data from sync is already in the password store.
PasswordEntryMap::iterator existing_local_entry_iter = PasswordEntryMap::iterator existing_local_entry_iter =
umatched_data_from_password_db->find(tag); unmatched_data_from_password_db->find(tag);
base::Time time_now = base::Time::Now(); base::Time time_now = base::Time::Now();
if (existing_local_entry_iter == umatched_data_from_password_db->end()) { if (existing_local_entry_iter == unmatched_data_from_password_db->end()) {
// The sync data is not in the password store, so we need to create it in // The sync data is not in the password store, so we need to create it in
// the password store. Add the entry to the new_entries list. // the password store. Add the entry to the new_entries list.
scoped_ptr<autofill::PasswordForm> new_password(new autofill::PasswordForm); AppendPasswordFromSpecifics(password_specifics, time_now,
new_password->date_synced = time_now; &sync_entries->new_entries);
PasswordFromSpecifics(password_specifics, new_password.get());
new_sync_entries->push_back(new_password.release());
} else { } else {
// The entry is in password store. If the entries are not identical, then // The entry is in password store. If the entries are not identical, then
// the entries need to be merged. // the entries need to be merged.
scoped_ptr<autofill::PasswordForm> new_password(new autofill::PasswordForm); // If the passwords differ, take the one that was created more recently.
switch (MergeLocalAndSyncPasswords(password_specifics, const autofill::PasswordForm& password_form =
*existing_local_entry_iter->second, *existing_local_entry_iter->second;
new_password.get())) { if (!AreLocalAndSyncPasswordsEqual(password_specifics, password_form)) {
case IDENTICAL: if (base::Time::FromInternalValue(password_specifics.date_created()) <
break; password_form.date_created) {
case SYNC:
new_password->date_synced = time_now;
updated_sync_entries->push_back(new_password.release());
break;
case LOCAL:
updated_db_entries->push_back( updated_db_entries->push_back(
syncer::SyncChange(FROM_HERE, syncer::SyncChange(FROM_HERE,
syncer::SyncChange::ACTION_UPDATE, syncer::SyncChange::ACTION_UPDATE,
SyncDataFromPassword(*new_password))); SyncDataFromPassword(password_form)));
break; } else {
AppendPasswordFromSpecifics(password_specifics, time_now,
&sync_entries->updated_entries);
}
} }
// Remove the entry from the entry map to indicate a match has been found. // Remove the entry from the entry map to indicate a match has been found.
// Entries that remain in the map at the end of associating all sync entries // Entries that remain in the map at the end of associating all sync entries
// will be treated as additions that need to be propagated to sync. // will be treated as additions that need to be propagated to sync.
umatched_data_from_password_db->erase(existing_local_entry_iter); unmatched_data_from_password_db->erase(existing_local_entry_iter);
}
}
void PasswordSyncableService::WriteEntriesToDatabase(
DatabaseOperation operation,
const PasswordForms& entries,
PasswordStoreChangeList* all_changes) {
for (PasswordForms::const_iterator it = entries.begin();
it != entries.end(); ++it) {
PasswordStoreChangeList new_changes = (password_store_->*operation)(**it);
all_changes->insert(all_changes->end(),
new_changes.begin(),
new_changes.end());
} }
} }
...@@ -418,60 +372,67 @@ syncer::SyncData SyncDataFromPassword( ...@@ -418,60 +372,67 @@ syncer::SyncData SyncDataFromPassword(
sync_pb::EntitySpecifics password_data; sync_pb::EntitySpecifics password_data;
sync_pb::PasswordSpecificsData* password_specifics = sync_pb::PasswordSpecificsData* password_specifics =
password_data.mutable_password()->mutable_client_only_encrypted_data(); password_data.mutable_password()->mutable_client_only_encrypted_data();
password_specifics->set_scheme(password_form.scheme); #define CopyField(field) password_specifics->set_ ## field(password_form.field)
password_specifics->set_signon_realm(password_form.signon_realm); #define CopyStringField(field) \
password_specifics->set_ ## field(base::UTF16ToUTF8(password_form.field))
CopyField(scheme);
CopyField(signon_realm);
password_specifics->set_origin(password_form.origin.spec()); password_specifics->set_origin(password_form.origin.spec());
password_specifics->set_action(password_form.action.spec()); password_specifics->set_action(password_form.action.spec());
password_specifics->set_username_element( CopyStringField(username_element);
base::UTF16ToUTF8(password_form.username_element)); CopyStringField(password_element);
password_specifics->set_password_element( CopyStringField(username_value);
base::UTF16ToUTF8(password_form.password_element)); CopyStringField(password_value);
password_specifics->set_username_value( CopyField(ssl_valid);
base::UTF16ToUTF8(password_form.username_value)); CopyField(preferred);
password_specifics->set_password_value(
base::UTF16ToUTF8(password_form.password_value));
password_specifics->set_ssl_valid(password_form.ssl_valid);
password_specifics->set_preferred(password_form.preferred);
password_specifics->set_date_created( password_specifics->set_date_created(
password_form.date_created.ToInternalValue()); password_form.date_created.ToInternalValue());
password_specifics->set_blacklisted(password_form.blacklisted_by_user); password_specifics->set_blacklisted(password_form.blacklisted_by_user);
password_specifics->set_type(password_form.type); CopyField(type);
password_specifics->set_times_used(password_form.times_used); CopyField(times_used);
#undef CopyStringField
#undef CopyField
std::string tag = MakePasswordSyncTag(*password_specifics); std::string tag = MakePasswordSyncTag(*password_specifics);
return syncer::SyncData::CreateLocalData(tag, tag, password_data); return syncer::SyncData::CreateLocalData(tag, tag, password_data);
} }
void PasswordFromSpecifics(const sync_pb::PasswordSpecificsData& password, autofill::PasswordForm PasswordFromSpecifics(
autofill::PasswordForm* new_password) { const sync_pb::PasswordSpecificsData& password) {
new_password->scheme = autofill::PasswordForm new_password;
new_password.scheme =
static_cast<autofill::PasswordForm::Scheme>(password.scheme()); static_cast<autofill::PasswordForm::Scheme>(password.scheme());
new_password->signon_realm = password.signon_realm(); new_password.signon_realm = password.signon_realm();
new_password->origin = GURL(password.origin()); new_password.origin = GURL(password.origin());
new_password->action = GURL(password.action()); new_password.action = GURL(password.action());
new_password->username_element = new_password.username_element =
base::UTF8ToUTF16(password.username_element()); base::UTF8ToUTF16(password.username_element());
new_password->password_element = new_password.password_element =
base::UTF8ToUTF16(password.password_element()); base::UTF8ToUTF16(password.password_element());
new_password->username_value = base::UTF8ToUTF16(password.username_value()); new_password.username_value = base::UTF8ToUTF16(password.username_value());
new_password->password_value = base::UTF8ToUTF16(password.password_value()); new_password.password_value = base::UTF8ToUTF16(password.password_value());
new_password->ssl_valid = password.ssl_valid(); new_password.ssl_valid = password.ssl_valid();
new_password->preferred = password.preferred(); new_password.preferred = password.preferred();
new_password->date_created = new_password.date_created =
base::Time::FromInternalValue(password.date_created()); base::Time::FromInternalValue(password.date_created());
new_password->blacklisted_by_user = password.blacklisted(); new_password.blacklisted_by_user = password.blacklisted();
new_password->type = new_password.type =
static_cast<autofill::PasswordForm::Type>(password.type()); static_cast<autofill::PasswordForm::Type>(password.type());
new_password->times_used = password.times_used(); new_password.times_used = password.times_used();
return new_password;
} }
std::string MakePasswordSyncTag( std::string MakePasswordSyncTag(
const sync_pb::PasswordSpecificsData& password) { const sync_pb::PasswordSpecificsData& password) {
return MakePasswordSyncTag(password.origin(), return MakePasswordSyncTag(PasswordFromSpecifics(password));
password.username_element(), }
password.username_value(),
password.password_element(), std::string MakePasswordSyncTag(const autofill::PasswordForm& password) {
password.signon_realm()); return (net::EscapePath(password.origin.spec()) + "|" +
net::EscapePath(base::UTF16ToUTF8(password.username_element)) + "|" +
net::EscapePath(base::UTF16ToUTF8(password.username_value)) + "|" +
net::EscapePath(base::UTF16ToUTF8(password.password_element)) + "|" +
net::EscapePath(password.signon_realm));
} }
} // namespace password_manager } // namespace password_manager
...@@ -34,14 +34,17 @@ namespace password_manager { ...@@ -34,14 +34,17 @@ namespace password_manager {
class PasswordStoreSync; class PasswordStoreSync;
// The implementation of the SyncableService API for passwords.
class PasswordSyncableService : public syncer::SyncableService, class PasswordSyncableService : public syncer::SyncableService,
public base::NonThreadSafe { public base::NonThreadSafe {
public: public:
// |PasswordSyncableService| is owned by |PasswordStore|. // Since the constructed |PasswordSyncableService| is typically owned by the
// |password_store|, the constructor doesn't take ownership of the
// |password_store|.
explicit PasswordSyncableService(PasswordStoreSync* password_store); explicit PasswordSyncableService(PasswordStoreSync* password_store);
virtual ~PasswordSyncableService(); virtual ~PasswordSyncableService();
// syncer::SyncableServiceImplementations // syncer::SyncableService:
virtual syncer::SyncMergeResult MergeDataAndStartSyncing( virtual syncer::SyncMergeResult MergeDataAndStartSyncing(
syncer::ModelType type, syncer::ModelType type,
const syncer::SyncDataList& initial_sync_data, const syncer::SyncDataList& initial_sync_data,
...@@ -54,11 +57,11 @@ class PasswordSyncableService : public syncer::SyncableService, ...@@ -54,11 +57,11 @@ class PasswordSyncableService : public syncer::SyncableService,
const tracked_objects::Location& from_here, const tracked_objects::Location& from_here,
const syncer::SyncChangeList& change_list) OVERRIDE; const syncer::SyncChangeList& change_list) OVERRIDE;
// Notifies sync of changes to the password database. // Notifies the Sync engine of changes to the password database.
void ActOnPasswordStoreChanges(const PasswordStoreChangeList& changes); void ActOnPasswordStoreChanges(const PasswordStoreChangeList& changes);
// Provides a StartSyncFlare to the SyncableService. See // Provides a StartSyncFlare to the SyncableService. See
// sync_start_util for more. // chrome/browser/sync/glue/sync_start_util.h for more.
void InjectStartSyncFlare( void InjectStartSyncFlare(
const syncer::SyncableService::StartSyncFlare& flare); const syncer::SyncableService::StartSyncFlare& flare);
...@@ -67,49 +70,49 @@ class PasswordSyncableService : public syncer::SyncableService, ...@@ -67,49 +70,49 @@ class PasswordSyncableService : public syncer::SyncableService,
// Map from password sync tag to password form. // Map from password sync tag to password form.
typedef std::map<std::string, autofill::PasswordForm*> PasswordEntryMap; typedef std::map<std::string, autofill::PasswordForm*> PasswordEntryMap;
// Helper function to retrieve the entries from password db and fill both // The type of PasswordStoreSync::AddLoginImpl,
// |password_entries| and |passwords_entry_map|. |passwords_entry_map| can be // PasswordStoreSync::UpdateLoginImpl and PasswordStoreSync::RemoveLoginImpl.
// NULL. typedef PasswordStoreChangeList(PasswordStoreSync::*DatabaseOperation)(
const autofill::PasswordForm& form);
struct SyncEntries;
// Retrieves the entries from password db and fills both |password_entries|
// and |passwords_entry_map|. |passwords_entry_map| can be NULL.
bool ReadFromPasswordStore( bool ReadFromPasswordStore(
ScopedVector<autofill::PasswordForm>* password_entries, ScopedVector<autofill::PasswordForm>* password_entries,
PasswordEntryMap* passwords_entry_map) const; PasswordEntryMap* passwords_entry_map) const;
// Uses the |PasswordStore| APIs to change entries. // Uses the |PasswordStore| APIs to change entries.
void WriteToPasswordStore(const PasswordForms& new_entries, void WriteToPasswordStore(const SyncEntries& entries);
const PasswordForms& updated_entries,
const PasswordForms& deleted_entries); // Examines |data|, an entry in sync db, and updates |sync_entries| or
// |updated_db_entries| accordingly. An element is removed from
// Notifies password store of a change that was performed by sync. // |unmatched_data_from_password_db| if its tag is identical to |data|'s.
// Virtual so tests can override.
virtual void NotifyPasswordStoreOfLoginChanges(
const PasswordStoreChangeList& changes);
// Checks if |data|, the entry in sync db, needs to be created or updated
// in the passwords db. Depending on what action needs to be performed, the
// entry may be added to |new_sync_entries| or to |updated_sync_entries|. If
// the item is identical to an entry in the passwords db, no action is
// performed. If an item needs to be updated in the sync db, then the item is
// also added to |updated_db_entries| list. If |data|'s tag is identical to
// an entry's tag in |umatched_data_from_password_db| then that entry will be
// removed from |umatched_data_from_password_db|.
void CreateOrUpdateEntry( void CreateOrUpdateEntry(
const syncer::SyncData& data, const syncer::SyncData& data,
PasswordEntryMap* umatched_data_from_password_db, PasswordEntryMap* unmatched_data_from_password_db,
ScopedVector<autofill::PasswordForm>* new_sync_entries, SyncEntries* sync_entries,
ScopedVector<autofill::PasswordForm>* updated_sync_entries,
syncer::SyncChangeList* updated_db_entries); syncer::SyncChangeList* updated_db_entries);
// Calls |operation| for each element in |entries| and appends the changes to
// |all_changes|.
void WriteEntriesToDatabase(
DatabaseOperation operation,
const PasswordForms& entries,
PasswordStoreChangeList* all_changes);
// The factory that creates sync errors. |SyncError| has rich data // The factory that creates sync errors. |SyncError| has rich data
// suitable for debugging. // suitable for debugging.
scoped_ptr<syncer::SyncErrorFactory> sync_error_factory_; scoped_ptr<syncer::SyncErrorFactory> sync_error_factory_;
// |SyncProcessor| will mirror the |PasswordStore| changes in the sync db. // |sync_processor_| will mirror the |PasswordStore| changes in the sync db.
scoped_ptr<syncer::SyncChangeProcessor> sync_processor_; scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
// The password store that adds/updates/deletes password entries. // The password store that adds/updates/deletes password entries. Not owned.
PasswordStoreSync* const password_store_; PasswordStoreSync* const password_store_;
// A signal to start sync as soon as possible. // A signal activated by this class to start sync as soon as possible.
syncer::SyncableService::StartSyncFlare flare_; syncer::SyncableService::StartSyncFlare flare_;
// True if processing sync changes is in progress. // True if processing sync changes is in progress.
...@@ -118,17 +121,6 @@ class PasswordSyncableService : public syncer::SyncableService, ...@@ -118,17 +121,6 @@ class PasswordSyncableService : public syncer::SyncableService,
DISALLOW_COPY_AND_ASSIGN(PasswordSyncableService); DISALLOW_COPY_AND_ASSIGN(PasswordSyncableService);
}; };
// Converts the |password| into a SyncData object.
syncer::SyncData SyncDataFromPassword(const autofill::PasswordForm& password);
// Extracts the |PasswordForm| data from sync's protobuffer format.
void PasswordFromSpecifics(const sync_pb::PasswordSpecificsData& password,
autofill::PasswordForm* new_password);
// Returns the unique tag that will serve as the sync identifier for the
// |password| entry.
std::string MakePasswordSyncTag(const sync_pb::PasswordSpecificsData& password);
} // namespace password_manager } // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_SYNCABLE_SERVICE_H__ #endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_SYNCABLE_SERVICE_H__
...@@ -23,43 +23,84 @@ using syncer::SyncChange; ...@@ -23,43 +23,84 @@ using syncer::SyncChange;
using syncer::SyncData; using syncer::SyncData;
using syncer::SyncDataList; using syncer::SyncDataList;
using syncer::SyncError; using syncer::SyncError;
using testing::AnyNumber;
using testing::ElementsAre;
using testing::Invoke; using testing::Invoke;
using testing::IsEmpty;
using testing::Matches;
using testing::Return; using testing::Return;
using testing::SetArgPointee; using testing::SetArgPointee;
using testing::UnorderedElementsAre;
using testing::_; using testing::_;
namespace password_manager { namespace password_manager {
// Defined in the implementation file corresponding to this test.
syncer::SyncData SyncDataFromPassword(const autofill::PasswordForm& password);
autofill::PasswordForm PasswordFromSpecifics(
const sync_pb::PasswordSpecificsData& password);
std::string MakePasswordSyncTag(const sync_pb::PasswordSpecificsData& password);
std::string MakePasswordSyncTag(const autofill::PasswordForm& password);
namespace { namespace {
typedef std::vector<SyncChange> SyncChangeList; typedef std::vector<SyncChange> SyncChangeList;
const sync_pb::PasswordSpecificsData& GetPasswordSpecifics( const sync_pb::PasswordSpecificsData& GetPasswordSpecifics(
const syncer::SyncData& sync_data) { const syncer::SyncData& sync_data) {
const sync_pb::EntitySpecifics& specifics = sync_data.GetSpecifics(); return sync_data.GetSpecifics().password().client_only_encrypted_data();
return specifics.password().client_only_encrypted_data(); }
MATCHER(HasDateSynced, "") {
return !arg.date_synced.is_null() && !arg.date_synced.is_max();
}
MATCHER_P(PasswordIs, form, "") {
sync_pb::PasswordSpecificsData actual_password =
GetPasswordSpecifics(SyncDataFromPassword(arg));
sync_pb::PasswordSpecificsData expected_password =
GetPasswordSpecifics(SyncDataFromPassword(form));
if (expected_password.scheme() == actual_password.scheme() &&
expected_password.signon_realm() == actual_password.signon_realm() &&
expected_password.origin() == actual_password.origin() &&
expected_password.action() == actual_password.action() &&
expected_password.username_element() ==
actual_password.username_element() &&
expected_password.password_element() ==
actual_password.password_element() &&
expected_password.username_value() == actual_password.username_value() &&
expected_password.password_value() == actual_password.password_value() &&
expected_password.ssl_valid() == actual_password.ssl_valid() &&
expected_password.preferred() == actual_password.preferred() &&
expected_password.date_created() == actual_password.date_created() &&
expected_password.blacklisted() == actual_password.blacklisted() &&
expected_password.type() == actual_password.type() &&
expected_password.times_used() == actual_password.times_used())
return true;
*result_listener << "Password protobuf does not match; expected:\n"
<< form << '\n'
<< "actual:" << '\n'
<< arg;
return false;
} }
void PasswordsEqual(const sync_pb::PasswordSpecificsData& expected_password, MATCHER_P2(SyncChangeIs, change_type, password, "") {
const sync_pb::PasswordSpecificsData& actual_password) { const SyncData& data = arg.sync_data();
EXPECT_EQ(expected_password.scheme(), actual_password.scheme()); autofill::PasswordForm form = PasswordFromSpecifics(
EXPECT_EQ(expected_password.signon_realm(), actual_password.signon_realm()); GetPasswordSpecifics(data));
EXPECT_EQ(expected_password.origin(), actual_password.origin()); return (arg.change_type() == change_type &&
EXPECT_EQ(expected_password.action(), actual_password.action()); syncer::SyncDataLocal(data).GetTag() ==
EXPECT_EQ(expected_password.username_element(), MakePasswordSyncTag(password) &&
actual_password.username_element()); (change_type == SyncChange::ACTION_DELETE ||
EXPECT_EQ(expected_password.password_element(), Matches(PasswordIs(password))(form)));
actual_password.password_element()); }
EXPECT_EQ(expected_password.username_value(),
actual_password.username_value()); // The argument is std::vector<autofill::PasswordForm*>*. The caller is
EXPECT_EQ(expected_password.password_value(), // responsible for the lifetime of all the password forms.
actual_password.password_value()); ACTION_P(AppendForm, form) {
EXPECT_EQ(expected_password.ssl_valid(), actual_password.ssl_valid()); arg0->push_back(new autofill::PasswordForm(form));
EXPECT_EQ(expected_password.preferred(), actual_password.preferred()); return true;
EXPECT_EQ(expected_password.date_created(), actual_password.date_created());
EXPECT_EQ(expected_password.blacklisted(), actual_password.blacklisted());
EXPECT_EQ(expected_password.type(), actual_password.type());
EXPECT_EQ(expected_password.times_used(), actual_password.times_used());
} }
// Creates a sync data consisting of password specifics. The sign on realm is // Creates a sync data consisting of password specifics. The sign on realm is
...@@ -82,229 +123,51 @@ SyncChange CreateSyncChange(const autofill::PasswordForm& password, ...@@ -82,229 +123,51 @@ SyncChange CreateSyncChange(const autofill::PasswordForm& password,
return SyncChange(FROM_HERE, type, data); return SyncChange(FROM_HERE, type, data);
} }
class FormFinder {
public:
explicit FormFinder(const autofill::PasswordForm& form) : form_(form) {}
~FormFinder() {}
bool operator()(const autofill::PasswordForm& form) const;
private:
const autofill::PasswordForm form_;
};
bool FormFinder::operator()(const autofill::PasswordForm& form) const {
return form.origin == form_.origin &&
form.username_element == form_.username_element &&
form.username_value == form_.username_value &&
form.password_element == form_.password_element &&
form.signon_realm == form_.signon_realm;
}
// A testable implementation of the |PasswordSyncableService| that mocks // A testable implementation of the |PasswordSyncableService| that mocks
// out all interaction with the password database. // out all interaction with the password database.
class MockPasswordSyncableService : public PasswordSyncableService { class MockPasswordSyncableService : public PasswordSyncableService {
public: public:
explicit MockPasswordSyncableService(PasswordStoreSync* password_store) explicit MockPasswordSyncableService(PasswordStoreSync* password_store)
: PasswordSyncableService(password_store) {} : PasswordSyncableService(password_store) {}
virtual ~MockPasswordSyncableService() {}
MOCK_METHOD1(NotifyPasswordStoreOfLoginChanges,
void (const PasswordStoreChangeList&));
MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType)); MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType));
};
// Class to verify the arguments passed to |PasswordStore|.
class PasswordStoreDataVerifier {
public:
PasswordStoreDataVerifier() {}
~PasswordStoreDataVerifier() {
EXPECT_TRUE(expected_db_add_changes_.empty());
EXPECT_TRUE(expected_db_update_changes_.empty());
EXPECT_TRUE(expected_db_delete_changes_.empty());
}
class TestSyncChangeProcessor;
// Sets expected changes to the password database.
void SetExpectedDBChanges(
const SyncDataList& add_forms,
const std::vector<autofill::PasswordForm*>& update_forms,
const std::vector<autofill::PasswordForm*>& delete_forms,
MockPasswordStore* password_store);
// Sets expected changes to TestSyncChangeProcessor.
void SetExpectedSyncChanges(SyncChangeList list);
private: private:
// Checks that |change_list| matches |expected_sync_change_list_|. DISALLOW_COPY_AND_ASSIGN(MockPasswordSyncableService);
SyncError TestSyncChanges(const SyncChangeList& change_list);
// Verifies that the |password| is present in the |expected_db_add_changes_|
// list. If found, |password| would be removed from
// |expected_db_add_changes_| list.
PasswordStoreChangeList VerifyAdd(const autofill::PasswordForm& password) {
return VerifyChange(PasswordStoreChange::ADD, password,
&expected_db_add_changes_);
}
// Verifies that the |password| is present in the
// |expected_db_update_changes_| list. If found, |password| would be removed
// from |expected_db_update_changes_| list.
PasswordStoreChangeList VerifyUpdate(const autofill::PasswordForm& password) {
return VerifyChange(PasswordStoreChange::UPDATE, password,
&expected_db_update_changes_);
}
// Verifies that the |password| is present in the
// |expected_db_delete_changes_| list. If found, |password| would be removed
// from |expected_db_delete_changes_| list.
PasswordStoreChangeList VerifyDelete(const autofill::PasswordForm& password) {
return VerifyChange(PasswordStoreChange::REMOVE, password,
&expected_db_delete_changes_);
}
static PasswordStoreChangeList VerifyChange(
PasswordStoreChange::Type type,
const autofill::PasswordForm& password,
std::vector<autofill::PasswordForm>* password_list);
std::vector<autofill::PasswordForm> expected_db_add_changes_;
std::vector<autofill::PasswordForm> expected_db_update_changes_;
std::vector<autofill::PasswordForm> expected_db_delete_changes_;
SyncChangeList expected_sync_change_list_;
DISALLOW_COPY_AND_ASSIGN(PasswordStoreDataVerifier);
}; };
class PasswordStoreDataVerifier::TestSyncChangeProcessor // Mock implementation of SyncChangeProcessor.
: public syncer::SyncChangeProcessor { class MockSyncChangeProcessor : public syncer::SyncChangeProcessor {
public: public:
explicit TestSyncChangeProcessor(PasswordStoreDataVerifier* verifier) MockSyncChangeProcessor() {}
: verifier_(verifier) {
}
virtual ~TestSyncChangeProcessor() {}
virtual SyncError ProcessSyncChanges(const tracked_objects::Location&,
const SyncChangeList& list) OVERRIDE {
return verifier_->TestSyncChanges(list);
}
MOCK_METHOD2(ProcessSyncChanges,
SyncError(const tracked_objects::Location&,
const SyncChangeList& list));
virtual SyncDataList GetAllSyncData(syncer::ModelType type) const OVERRIDE { virtual SyncDataList GetAllSyncData(syncer::ModelType type) const OVERRIDE {
NOTREACHED();
return SyncDataList(); return SyncDataList();
} }
private:
PasswordStoreDataVerifier* verifier_;
DISALLOW_COPY_AND_ASSIGN(TestSyncChangeProcessor); private:
DISALLOW_COPY_AND_ASSIGN(MockSyncChangeProcessor);
}; };
void PasswordStoreDataVerifier::SetExpectedDBChanges( // Convenience wrapper around a PasswordSyncableService and PasswordStore
const SyncDataList& add_forms, // pair.
const std::vector<autofill::PasswordForm*>& update_forms,
const std::vector<autofill::PasswordForm*>& delete_forms,
MockPasswordStore* password_store) {
DCHECK(expected_db_add_changes_.empty());
DCHECK(expected_db_update_changes_.empty());
DCHECK(password_store);
for (SyncDataList::const_iterator it = add_forms.begin();
it != add_forms.end(); ++it) {
autofill::PasswordForm form;
PasswordFromSpecifics(GetPasswordSpecifics(*it), &form);
expected_db_add_changes_.push_back(form);
}
if (expected_db_add_changes_.empty()) {
EXPECT_CALL(*password_store, AddLoginImpl(_)).Times(0);
} else {
EXPECT_CALL(*password_store, AddLoginImpl(_))
.Times(expected_db_add_changes_.size())
.WillRepeatedly(Invoke(this, &PasswordStoreDataVerifier::VerifyAdd));
}
for (std::vector<autofill::PasswordForm*>::const_iterator it =
update_forms.begin();
it != update_forms.end(); ++it) {
expected_db_update_changes_.push_back(**it);
}
if (expected_db_update_changes_.empty()) {
EXPECT_CALL(*password_store, UpdateLoginImpl(_)).Times(0);
} else {
EXPECT_CALL(*password_store, UpdateLoginImpl(_))
.Times(expected_db_update_changes_.size())
.WillRepeatedly(Invoke(this, &PasswordStoreDataVerifier::VerifyUpdate));
}
for (std::vector<autofill::PasswordForm*>::const_iterator it =
delete_forms.begin();
it != delete_forms.end(); ++it) {
expected_db_delete_changes_.push_back(**it);
}
if (expected_db_delete_changes_.empty()) {
EXPECT_CALL(*password_store, RemoveLoginImpl(_)).Times(0);
} else {
EXPECT_CALL(*password_store, RemoveLoginImpl(_))
.Times(expected_db_delete_changes_.size())
.WillRepeatedly(Invoke(this, &PasswordStoreDataVerifier::VerifyDelete));
}
}
void PasswordStoreDataVerifier::SetExpectedSyncChanges(SyncChangeList list) {
expected_sync_change_list_.swap(list);
}
SyncError PasswordStoreDataVerifier::TestSyncChanges(
const SyncChangeList& change_list) {
for (SyncChangeList::const_iterator it = change_list.begin();
it != change_list.end(); ++it) {
SyncData data = it->sync_data();
std::string actual_tag = syncer::SyncDataLocal(data).GetTag();
bool matched = false;
for (SyncChangeList::iterator expected_it =
expected_sync_change_list_.begin();
expected_it != expected_sync_change_list_.end();
++expected_it) {
const sync_pb::PasswordSpecificsData& expected_password(
GetPasswordSpecifics(expected_it->sync_data()));
if (actual_tag == MakePasswordSyncTag(expected_password)) {
EXPECT_EQ(expected_it->change_type(), it->change_type());
matched = true;
if (it->change_type() != SyncChange::ACTION_DELETE)
PasswordsEqual(expected_password, GetPasswordSpecifics(data));
break;
}
}
EXPECT_TRUE(matched) << actual_tag;
}
EXPECT_EQ(expected_sync_change_list_.size(), change_list.size());
return SyncError();
}
// static
PasswordStoreChangeList PasswordStoreDataVerifier::VerifyChange(
PasswordStoreChange::Type type,
const autofill::PasswordForm& password,
std::vector<autofill::PasswordForm>* password_list) {
std::vector<autofill::PasswordForm>::iterator it = std::find_if(
password_list->begin(), password_list->end(), FormFinder(password));
EXPECT_NE(password_list->end(), it);
PasswordsEqual(GetPasswordSpecifics(SyncDataFromPassword(*it)),
GetPasswordSpecifics(SyncDataFromPassword(password)));
if (type != PasswordStoreChange::REMOVE) {
EXPECT_FALSE(password.date_synced.is_null()) << password.signon_realm;
EXPECT_FALSE(password.date_synced.is_max()) << password.signon_realm;
}
password_list->erase(it);
return PasswordStoreChangeList(1, PasswordStoreChange(type, password));
}
class PasswordSyncableServiceWrapper { class PasswordSyncableServiceWrapper {
public: public:
PasswordSyncableServiceWrapper() { PasswordSyncableServiceWrapper() {
password_store_ = new MockPasswordStore; password_store_ = new testing::StrictMock<MockPasswordStore>;
service_.reset(new MockPasswordSyncableService( service_.reset(new MockPasswordSyncableService(
password_store_->GetSyncInterface())); password_store_->GetSyncInterface()));
ON_CALL(*password_store_, AddLoginImpl(HasDateSynced()))
.WillByDefault(Return(PasswordStoreChangeList()));
ON_CALL(*password_store_, RemoveLoginImpl(_))
.WillByDefault(Return(PasswordStoreChangeList()));
ON_CALL(*password_store_, UpdateLoginImpl(HasDateSynced()))
.WillByDefault(Return(PasswordStoreChangeList()));
EXPECT_CALL(*password_store(), NotifyLoginsChanged(_)).Times(AnyNumber());
} }
~PasswordSyncableServiceWrapper() { ~PasswordSyncableServiceWrapper() {
...@@ -313,207 +176,164 @@ class PasswordSyncableServiceWrapper { ...@@ -313,207 +176,164 @@ class PasswordSyncableServiceWrapper {
MockPasswordStore* password_store() { return password_store_.get(); } MockPasswordStore* password_store() { return password_store_.get(); }
MockPasswordSyncableService* service() { MockPasswordSyncableService* service() { return service_.get(); }
return service_.get();
}
// Returnes the scoped_ptr to |service_| thus NULLing out it. // Returnes the scoped_ptr to |service_| thus NULLing out it.
scoped_ptr<syncer::SyncChangeProcessor> ReleaseSyncableService() { scoped_ptr<syncer::SyncChangeProcessor> ReleaseSyncableService() {
return service_.PassAs<syncer::SyncChangeProcessor>(); return service_.PassAs<syncer::SyncChangeProcessor>();
} }
PasswordStoreDataVerifier* verifier() { private:
return &verifier_;
}
scoped_ptr<syncer::SyncChangeProcessor> CreateSyncChangeProcessor() {
return make_scoped_ptr<syncer::SyncChangeProcessor>(
new PasswordStoreDataVerifier::TestSyncChangeProcessor(verifier()));
}
// Sets the data that will be returned to the caller accessing password store.
void SetPasswordStoreData(
const std::vector<autofill::PasswordForm*>& forms,
const std::vector<autofill::PasswordForm*>& blacklist_forms) {
EXPECT_CALL(*password_store_.get(), FillAutofillableLogins(_))
.WillOnce(Invoke(AppendVector(forms)))
.RetiresOnSaturation();
EXPECT_CALL(*password_store_.get(), FillBlacklistLogins(_))
.WillOnce(Invoke(AppendVector(blacklist_forms)))
.RetiresOnSaturation();
}
protected:
scoped_refptr<MockPasswordStore> password_store_; scoped_refptr<MockPasswordStore> password_store_;
scoped_ptr<MockPasswordSyncableService> service_; scoped_ptr<MockPasswordSyncableService> service_;
PasswordStoreDataVerifier verifier_;
private:
struct AppendVector {
explicit AppendVector(
const std::vector<autofill::PasswordForm*>& append_forms)
: append_forms_(append_forms) {
}
~AppendVector() {}
bool operator()(std::vector<autofill::PasswordForm*>* forms) const {
forms->insert(forms->end(), append_forms_.begin(), append_forms_.end());
return true;
}
std::vector<autofill::PasswordForm*> append_forms_;
};
DISALLOW_COPY_AND_ASSIGN(PasswordSyncableServiceWrapper); DISALLOW_COPY_AND_ASSIGN(PasswordSyncableServiceWrapper);
}; };
class PasswordSyncableServiceTest : public testing::Test, class PasswordSyncableServiceTest : public testing::Test {
public PasswordSyncableServiceWrapper {
public: public:
PasswordSyncableServiceTest() {} PasswordSyncableServiceTest()
virtual ~PasswordSyncableServiceTest() {} : processor_(new testing::StrictMock<MockSyncChangeProcessor>) {
ON_CALL(*processor_, ProcessSyncChanges(_, _))
.WillByDefault(Return(SyncError()));
}
MockPasswordStore* password_store() { return wrapper_.password_store(); }
MockPasswordSyncableService* service() { return wrapper_.service(); }
protected:
scoped_ptr<MockSyncChangeProcessor> processor_;
private:
PasswordSyncableServiceWrapper wrapper_;
}; };
// Both sync and password db have data that are not present in the other. // Both sync and password db have data that are not present in the other.
TEST_F(PasswordSyncableServiceTest, AdditionsInBoth) { TEST_F(PasswordSyncableServiceTest, AdditionsInBoth) {
scoped_ptr<autofill::PasswordForm> form1(new autofill::PasswordForm); autofill::PasswordForm form;
form1->signon_realm = "abc"; form.signon_realm = "abc";
std::vector<autofill::PasswordForm*> forms;
forms.push_back(form1.release());
SetPasswordStoreData(forms, std::vector<autofill::PasswordForm*>());
SyncData sync_data = CreateSyncData("def");
SyncDataList list; SyncDataList list;
list.push_back(sync_data); list.push_back(CreateSyncData("def"));
autofill::PasswordForm new_from_sync = PasswordFromSpecifics(
verifier()->SetExpectedDBChanges(list, GetPasswordSpecifics(list.back()));
std::vector<autofill::PasswordForm*>(),
std::vector<autofill::PasswordForm*>(), EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
password_store()); .WillOnce(AppendForm(form));
verifier()->SetExpectedSyncChanges( EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
SyncChangeList(1, CreateSyncChange(*forms[0], SyncChange::ACTION_ADD))); .WillOnce(Return(true));
EXPECT_CALL(*service(), NotifyPasswordStoreOfLoginChanges(_)); EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(new_from_sync)));
EXPECT_CALL(*processor_, ProcessSyncChanges(_, ElementsAre(
SyncChangeIs(SyncChange::ACTION_ADD, form))));
service()->MergeDataAndStartSyncing(syncer::PASSWORDS, service()->MergeDataAndStartSyncing(
syncer::PASSWORDS,
list, list,
CreateSyncChangeProcessor(), processor_.PassAs<syncer::SyncChangeProcessor>(),
scoped_ptr<syncer::SyncErrorFactory>()); scoped_ptr<syncer::SyncErrorFactory>());
} }
// Sync has data that is not present in the password db. // Sync has data that is not present in the password db.
TEST_F(PasswordSyncableServiceTest, AdditionOnlyInSync) { TEST_F(PasswordSyncableServiceTest, AdditionOnlyInSync) {
SetPasswordStoreData(std::vector<autofill::PasswordForm*>(),
std::vector<autofill::PasswordForm*>());
SyncData sync_data = CreateSyncData("def");
SyncDataList list; SyncDataList list;
list.push_back(sync_data); list.push_back(CreateSyncData("def"));
autofill::PasswordForm new_from_sync = PasswordFromSpecifics(
GetPasswordSpecifics(list.back()));
verifier()->SetExpectedDBChanges(list, EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
std::vector<autofill::PasswordForm*>(), .WillOnce(Return(true));
std::vector<autofill::PasswordForm*>(), EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
password_store()); .WillOnce(Return(true));
verifier()->SetExpectedSyncChanges(SyncChangeList()); EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(new_from_sync)));
EXPECT_CALL(*service(), NotifyPasswordStoreOfLoginChanges(_)); EXPECT_CALL(*processor_, ProcessSyncChanges(_, IsEmpty()));
service()->MergeDataAndStartSyncing(syncer::PASSWORDS, service()->MergeDataAndStartSyncing(
syncer::PASSWORDS,
list, list,
CreateSyncChangeProcessor(), processor_.PassAs<syncer::SyncChangeProcessor>(),
scoped_ptr<syncer::SyncErrorFactory>()); scoped_ptr<syncer::SyncErrorFactory>());
} }
// Passwords db has data that is not present in sync. // Passwords db has data that is not present in sync.
TEST_F(PasswordSyncableServiceTest, AdditionOnlyInPasswordStore) { TEST_F(PasswordSyncableServiceTest, AdditionOnlyInPasswordStore) {
scoped_ptr<autofill::PasswordForm> form1(new autofill::PasswordForm); autofill::PasswordForm form;
form1->signon_realm = "abc"; form.signon_realm = "abc";
form1->times_used = 2; form.times_used = 2;
form1->type = autofill::PasswordForm::TYPE_GENERATED; form.type = autofill::PasswordForm::TYPE_GENERATED;
std::vector<autofill::PasswordForm*> forms; EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
forms.push_back(form1.release()); .WillOnce(AppendForm(form));
SetPasswordStoreData(forms, std::vector<autofill::PasswordForm*>()); EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
.WillOnce(Return(true));
verifier()->SetExpectedDBChanges(SyncDataList(),
std::vector<autofill::PasswordForm*>(), EXPECT_CALL(*processor_, ProcessSyncChanges(_, ElementsAre(
std::vector<autofill::PasswordForm*>(), SyncChangeIs(SyncChange::ACTION_ADD, form))));
password_store());
verifier()->SetExpectedSyncChanges( service()->MergeDataAndStartSyncing(
SyncChangeList(1, CreateSyncChange(*forms[0], SyncChange::ACTION_ADD))); syncer::PASSWORDS,
EXPECT_CALL(*service_,
NotifyPasswordStoreOfLoginChanges(PasswordStoreChangeList()));
service()->MergeDataAndStartSyncing(syncer::PASSWORDS,
SyncDataList(), SyncDataList(),
CreateSyncChangeProcessor(), processor_.PassAs<syncer::SyncChangeProcessor>(),
scoped_ptr<syncer::SyncErrorFactory>()); scoped_ptr<syncer::SyncErrorFactory>());
} }
// Both passwords db and sync contain the same data. // Both passwords db and sync contain the same data.
TEST_F(PasswordSyncableServiceTest, BothInSync) { TEST_F(PasswordSyncableServiceTest, BothInSync) {
scoped_ptr<autofill::PasswordForm> form1(new autofill::PasswordForm); autofill::PasswordForm form;
form1->signon_realm = "abc"; form.signon_realm = "abc";
form1->times_used = 3; form.times_used = 3;
form1->type = autofill::PasswordForm::TYPE_GENERATED; form.type = autofill::PasswordForm::TYPE_GENERATED;
std::vector<autofill::PasswordForm*> forms; EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
forms.push_back(form1.release()); .WillOnce(AppendForm(form));
SetPasswordStoreData(forms, std::vector<autofill::PasswordForm*>()); EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
.WillOnce(Return(true));
verifier()->SetExpectedDBChanges(SyncDataList(),
std::vector<autofill::PasswordForm*>(), EXPECT_CALL(*processor_, ProcessSyncChanges(_, IsEmpty()));
std::vector<autofill::PasswordForm*>(),
password_store()); service()->MergeDataAndStartSyncing(
verifier()->SetExpectedSyncChanges(SyncChangeList()); syncer::PASSWORDS,
EXPECT_CALL(*service_,
NotifyPasswordStoreOfLoginChanges(PasswordStoreChangeList()));
service()->MergeDataAndStartSyncing(syncer::PASSWORDS,
SyncDataList(1, CreateSyncData("abc")), SyncDataList(1, CreateSyncData("abc")),
CreateSyncChangeProcessor(), processor_.PassAs<syncer::SyncChangeProcessor>(),
scoped_ptr<syncer::SyncErrorFactory>()); scoped_ptr<syncer::SyncErrorFactory>());
} }
// Both passwords db and sync have the same data but they need to be merged // Both passwords db and sync have the same data but they need to be merged
// as some fields of the data differ. // as some fields of the data differ.
TEST_F(PasswordSyncableServiceTest, Merge) { TEST_F(PasswordSyncableServiceTest, Merge) {
scoped_ptr<autofill::PasswordForm> form1(new autofill::PasswordForm); autofill::PasswordForm form1;
form1->signon_realm = "abc"; form1.signon_realm = "abc";
form1->action = GURL("http://pie.com"); form1.action = GURL("http://pie.com");
form1->date_created = base::Time::Now(); form1.date_created = base::Time::Now();
form1->preferred = true; form1.preferred = true;
std::vector<autofill::PasswordForm*> forms;
forms.push_back(form1.release()); autofill::PasswordForm form2(form1);
SetPasswordStoreData(forms, std::vector<autofill::PasswordForm*>());
autofill::PasswordForm form2(*forms[0]);
form2.preferred = false; form2.preferred = false;
verifier()->SetExpectedDBChanges(SyncDataList(), EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
std::vector<autofill::PasswordForm*>(1, .WillOnce(AppendForm(form1));
&form2), EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
std::vector<autofill::PasswordForm*>(), .WillOnce(Return(true));
password_store()); EXPECT_CALL(*password_store(), UpdateLoginImpl(PasswordIs(form2)));
verifier()->SetExpectedSyncChanges(SyncChangeList()); EXPECT_CALL(*processor_, ProcessSyncChanges(_, IsEmpty()));
EXPECT_CALL(*service(), NotifyPasswordStoreOfLoginChanges(_)); service()->MergeDataAndStartSyncing(
syncer::PASSWORDS,
service()->MergeDataAndStartSyncing(syncer::PASSWORDS, SyncDataList(1, SyncDataFromPassword(form2)),
SyncDataList(1, processor_.PassAs<syncer::SyncChangeProcessor>(),
SyncDataFromPassword(form2)),
CreateSyncChangeProcessor(),
scoped_ptr<syncer::SyncErrorFactory>()); scoped_ptr<syncer::SyncErrorFactory>());
} }
// Initiate sync due to local DB changes. // Initiate sync due to local DB changes.
TEST_F(PasswordSyncableServiceTest, PasswordStoreChanges) { TEST_F(PasswordSyncableServiceTest, PasswordStoreChanges) {
// Set the sync change processor first. // Save the reference to the processor because |processor_| is NULL after
SetPasswordStoreData(std::vector<autofill::PasswordForm*>(), // MergeDataAndStartSyncing().
std::vector<autofill::PasswordForm*>()); MockSyncChangeProcessor& weak_processor = *processor_;
verifier()->SetExpectedSyncChanges(SyncChangeList()); EXPECT_CALL(weak_processor, ProcessSyncChanges(_, IsEmpty()));
EXPECT_CALL(*service_, EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
NotifyPasswordStoreOfLoginChanges(PasswordStoreChangeList())); .WillOnce(Return(true));
service_->MergeDataAndStartSyncing(syncer::PASSWORDS, EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
.WillOnce(Return(true));
service()->MergeDataAndStartSyncing(
syncer::PASSWORDS,
SyncDataList(), SyncDataList(),
CreateSyncChangeProcessor(), processor_.PassAs<syncer::SyncChangeProcessor>(),
scoped_ptr<syncer::SyncErrorFactory>()); scoped_ptr<syncer::SyncErrorFactory>());
autofill::PasswordForm form1; autofill::PasswordForm form1;
...@@ -528,16 +348,14 @@ TEST_F(PasswordSyncableServiceTest, PasswordStoreChanges) { ...@@ -528,16 +348,14 @@ TEST_F(PasswordSyncableServiceTest, PasswordStoreChanges) {
sync_list.push_back(CreateSyncChange(form2, SyncChange::ACTION_UPDATE)); sync_list.push_back(CreateSyncChange(form2, SyncChange::ACTION_UPDATE));
sync_list.push_back(CreateSyncChange(form3, SyncChange::ACTION_DELETE)); sync_list.push_back(CreateSyncChange(form3, SyncChange::ACTION_DELETE));
verifier()->SetExpectedDBChanges(SyncDataList(),
std::vector<autofill::PasswordForm*>(),
std::vector<autofill::PasswordForm*>(),
password_store());
verifier()->SetExpectedSyncChanges(sync_list);
PasswordStoreChangeList list; PasswordStoreChangeList list;
list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form1)); list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form1));
list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form2)); list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form2));
list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form3)); list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form3));
EXPECT_CALL(weak_processor, ProcessSyncChanges(_, ElementsAre(
SyncChangeIs(SyncChange::ACTION_ADD, form1),
SyncChangeIs(SyncChange::ACTION_UPDATE, form2),
SyncChangeIs(SyncChange::ACTION_DELETE, form3))));
service()->ActOnPasswordStoreChanges(list); service()->ActOnPasswordStoreChanges(list);
} }
...@@ -553,12 +371,8 @@ TEST_F(PasswordSyncableServiceTest, ProcessSyncChanges) { ...@@ -553,12 +371,8 @@ TEST_F(PasswordSyncableServiceTest, ProcessSyncChanges) {
deleted_form.blacklisted_by_user = true; deleted_form.blacklisted_by_user = true;
SyncData add_data = CreateSyncData("def"); SyncData add_data = CreateSyncData("def");
std::vector<autofill::PasswordForm*> updated_passwords(1, &updated_form); autofill::PasswordForm new_from_sync = PasswordFromSpecifics(
std::vector<autofill::PasswordForm*> deleted_passwords(1, &deleted_form); GetPasswordSpecifics(add_data));
verifier()->SetExpectedDBChanges(SyncDataList(1, add_data),
updated_passwords,
deleted_passwords,
password_store());
SyncChangeList list; SyncChangeList list;
list.push_back(SyncChange(FROM_HERE, list.push_back(SyncChange(FROM_HERE,
...@@ -568,72 +382,62 @@ TEST_F(PasswordSyncableServiceTest, ProcessSyncChanges) { ...@@ -568,72 +382,62 @@ TEST_F(PasswordSyncableServiceTest, ProcessSyncChanges) {
syncer::SyncChange::ACTION_UPDATE)); syncer::SyncChange::ACTION_UPDATE));
list.push_back(CreateSyncChange(deleted_form, list.push_back(CreateSyncChange(deleted_form,
syncer::SyncChange::ACTION_DELETE)); syncer::SyncChange::ACTION_DELETE));
EXPECT_CALL(*service(), NotifyPasswordStoreOfLoginChanges(_)); EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(new_from_sync)));
EXPECT_CALL(*password_store(), UpdateLoginImpl(PasswordIs(updated_form)));
EXPECT_CALL(*password_store(), RemoveLoginImpl(PasswordIs(deleted_form)));
service()->ProcessSyncChanges(FROM_HERE, list); service()->ProcessSyncChanges(FROM_HERE, list);
} }
// Retrives sync data from the model. // Retrives sync data from the model.
TEST_F(PasswordSyncableServiceTest, GetAllSyncData) { TEST_F(PasswordSyncableServiceTest, GetAllSyncData) {
scoped_ptr<autofill::PasswordForm> form1(new autofill::PasswordForm); autofill::PasswordForm form1;
form1->signon_realm = "abc"; form1.signon_realm = "abc";
form1->action = GURL("http://foo.com"); form1.action = GURL("http://foo.com");
form1->times_used = 5; form1.times_used = 5;
form1->type = autofill::PasswordForm::TYPE_GENERATED; form1.type = autofill::PasswordForm::TYPE_GENERATED;
scoped_ptr<autofill::PasswordForm> form2(new autofill::PasswordForm); autofill::PasswordForm form2;
form2->signon_realm = "xyz"; form2.signon_realm = "xyz";
form2->action = GURL("http://bar.com"); form2.action = GURL("http://bar.com");
form2->blacklisted_by_user = true; form2.blacklisted_by_user = true;
std::vector<autofill::PasswordForm*> forms(1, form1.release()); EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
std::vector<autofill::PasswordForm*> blacklist_forms(1, form2.release()); .WillOnce(AppendForm(form1));
SetPasswordStoreData(forms, blacklist_forms); EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
.WillOnce(AppendForm(form2));
SyncDataList expected_list;
expected_list.push_back(SyncDataFromPassword(*forms[0]));
expected_list.push_back(SyncDataFromPassword(*blacklist_forms[0]));
verifier()->SetExpectedDBChanges(SyncDataList(),
std::vector<autofill::PasswordForm*>(),
std::vector<autofill::PasswordForm*>(),
password_store());
SyncDataList actual_list = service()->GetAllSyncData(syncer::PASSWORDS); SyncDataList actual_list = service()->GetAllSyncData(syncer::PASSWORDS);
EXPECT_EQ(expected_list.size(), actual_list.size()); std::vector<autofill::PasswordForm> actual_form_list;
for (SyncDataList::iterator i(actual_list.begin()), j(expected_list.begin()); for (SyncDataList::iterator it = actual_list.begin();
i != actual_list.end() && j != expected_list.end(); ++i, ++j) { it != actual_list.end(); ++it) {
PasswordsEqual(GetPasswordSpecifics(*j), GetPasswordSpecifics(*i)); actual_form_list.push_back(
PasswordFromSpecifics(GetPasswordSpecifics(*it)));
} }
EXPECT_THAT(actual_form_list, UnorderedElementsAre(PasswordIs(form1),
PasswordIs(form2)));
} }
// Creates 2 PasswordSyncableService instances, merges the content of the first // Creates 2 PasswordSyncableService instances, merges the content of the first
// one to the second one and back. // one to the second one and back.
TEST_F(PasswordSyncableServiceTest, MergeDataAndPushBack) { TEST_F(PasswordSyncableServiceTest, MergeDataAndPushBack) {
scoped_ptr<autofill::PasswordForm> form1(new autofill::PasswordForm); autofill::PasswordForm form1;
form1->signon_realm = "abc"; form1.signon_realm = "abc";
form1->action = GURL("http://foo.com"); form1.action = GURL("http://foo.com");
std::vector<autofill::PasswordForm*> forms(1, form1.release());
SetPasswordStoreData(forms, std::vector<autofill::PasswordForm*>());
PasswordSyncableServiceWrapper other_service_wrapper; PasswordSyncableServiceWrapper other_service_wrapper;
scoped_ptr<autofill::PasswordForm> form2(new autofill::PasswordForm); autofill::PasswordForm form2;
form2->signon_realm = "xyz"; form2.signon_realm = "xyz";
form2->action = GURL("http://bar.com"); form2.action = GURL("http://bar.com");
syncer::SyncData form2_sync_data = SyncDataFromPassword(*form2); EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
other_service_wrapper.SetPasswordStoreData( .WillOnce(AppendForm(form1));
std::vector<autofill::PasswordForm*>(1, form2.release()), EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
std::vector<autofill::PasswordForm*>()); .WillOnce(Return(true));
EXPECT_CALL(*other_service_wrapper.password_store(),
verifier()->SetExpectedDBChanges(SyncDataList(1, form2_sync_data), FillAutofillableLogins(_)).WillOnce(AppendForm(form2));
std::vector<autofill::PasswordForm*>(), EXPECT_CALL(*other_service_wrapper.password_store(),
std::vector<autofill::PasswordForm*>(), FillBlacklistLogins(_)).WillOnce(Return(true));
password_store());
other_service_wrapper.verifier()->SetExpectedDBChanges( EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(form2)));
SyncDataList(1, SyncDataFromPassword(*forms[0])), EXPECT_CALL(*other_service_wrapper.password_store(),
std::vector<autofill::PasswordForm*>(), AddLoginImpl(PasswordIs(form1)));
std::vector<autofill::PasswordForm*>(),
other_service_wrapper.password_store());
EXPECT_CALL(*service(), NotifyPasswordStoreOfLoginChanges(_));
EXPECT_CALL(*other_service_wrapper.service(),
NotifyPasswordStoreOfLoginChanges(_));
syncer::SyncDataList other_service_data = syncer::SyncDataList other_service_data =
other_service_wrapper.service()->GetAllSyncData(syncer::PASSWORDS); other_service_wrapper.service()->GetAllSyncData(syncer::PASSWORDS);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment