Index: components/password_manager/core/browser/password_syncable_service_unittest.cc |
diff --git a/components/password_manager/core/browser/password_syncable_service_unittest.cc b/components/password_manager/core/browser/password_syncable_service_unittest.cc |
index aa55f28b5499c2a0793869c466230b606389cac4..c487e6682cc49ea0d8e4d69edda91ff008d4dcea 100644 |
--- a/components/password_manager/core/browser/password_syncable_service_unittest.cc |
+++ b/components/password_manager/core/browser/password_syncable_service_unittest.cc |
@@ -14,9 +14,11 @@ |
#include "base/macros.h" |
#include "base/memory/ptr_util.h" |
#include "base/memory/ref_counted.h" |
-#include "base/message_loop/message_loop.h" |
#include "base/strings/utf_string_conversions.h" |
+#include "base/test/scoped_task_environment.h" |
+#include "base/test/simple_test_clock.h" |
#include "components/password_manager/core/browser/mock_password_store.h" |
+#include "components/password_manager/core/browser/password_manager_test_utils.h" |
#include "components/sync/model/sync_change_processor.h" |
#include "components/sync/model/sync_error.h" |
#include "components/sync/model/sync_error_factory_mock.h" |
@@ -51,17 +53,19 @@ std::string MakePasswordSyncTag(const autofill::PasswordForm& password); |
namespace { |
// PasswordForm values for tests. |
-const autofill::PasswordForm::Type kArbitraryType = |
+constexpr autofill::PasswordForm::Type kArbitraryType = |
autofill::PasswordForm::TYPE_GENERATED; |
-const char kIconUrl[] = "https://fb.com/Icon"; |
-const char kDisplayName[] = "Agent Smith"; |
-const char kFederationUrl[] = "https://fb.com/"; |
-const char kPassword[] = "abcdef"; |
-const char kSignonRealm[] = "abc"; |
-const char kSignonRealm2[] = "def"; |
-const char kSignonRealm3[] = "xyz"; |
-const int kTimesUsed = 5; |
-const char kUsername[] = "godzilla"; |
+constexpr char kAndroidAutofillRealm[] = "android://hash@com.magisto"; |
+constexpr char kAndroidCorrectRealm[] = "android://hash@com.magisto/"; |
+constexpr char kIconUrl[] = "https://fb.com/Icon"; |
+constexpr char kDisplayName[] = "Agent Smith"; |
+constexpr char kFederationUrl[] = "https://fb.com/"; |
+constexpr char kPassword[] = "abcdef"; |
+constexpr char kSignonRealm[] = "abc"; |
+constexpr char kSignonRealm2[] = "def"; |
+constexpr char kSignonRealm3[] = "xyz"; |
+constexpr int kTimesUsed = 5; |
+constexpr char kUsername[] = "godzilla"; |
typedef std::vector<SyncChange> SyncChangeList; |
@@ -70,8 +74,8 @@ const sync_pb::PasswordSpecificsData& GetPasswordSpecifics( |
return sync_data.GetSpecifics().password().client_only_encrypted_data(); |
} |
-MATCHER(HasDateSynced, "") { |
- return !arg.date_synced.is_null() && !arg.date_synced.is_max(); |
+MATCHER_P(HasDateSynced, time, "") { |
+ return arg.date_synced == time; |
} |
MATCHER_P(PasswordIs, form, "") { |
@@ -124,6 +128,14 @@ ACTION_P(AppendForm, form) { |
return true; |
} |
+// The argument is std::vector<autofill::PasswordForm*>*. The caller is |
+// responsible for the lifetime of all the password forms. |
+ACTION_P(AppendForms, forms) { |
+ for (const autofill::PasswordForm& form : forms) |
+ arg0->push_back(base::MakeUnique<autofill::PasswordForm>(form)); |
+ return true; |
+} |
+ |
// Creates a sync data consisting of password specifics. The sign on realm is |
// set to |signon_realm|. |
SyncData CreateSyncData(const std::string& signon_realm) { |
@@ -149,19 +161,6 @@ SyncChange CreateSyncChange(const autofill::PasswordForm& password, |
return SyncChange(FROM_HERE, type, data); |
} |
-// A testable implementation of the |PasswordSyncableService| that mocks |
-// out all interaction with the password database. |
-class MockPasswordSyncableService : public PasswordSyncableService { |
- public: |
- explicit MockPasswordSyncableService(PasswordStoreSync* password_store) |
- : PasswordSyncableService(password_store) {} |
- |
- MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType)); |
- |
- private: |
- DISALLOW_COPY_AND_ASSIGN(MockPasswordSyncableService); |
-}; |
- |
// Mock implementation of SyncChangeProcessor. |
class MockSyncChangeProcessor : public syncer::SyncChangeProcessor { |
public: |
@@ -186,12 +185,15 @@ class PasswordSyncableServiceWrapper { |
PasswordSyncableServiceWrapper() { |
password_store_ = new testing::StrictMock<MockPasswordStore>; |
service_.reset( |
- new MockPasswordSyncableService(password_store_->GetSyncInterface())); |
- ON_CALL(*password_store_, AddLoginImpl(HasDateSynced())) |
+ new PasswordSyncableService(password_store_->GetSyncInterface())); |
+ auto clock = base::MakeUnique<base::SimpleTestClock>(); |
+ clock->SetNow(time()); |
+ service_->set_clock(std::move(clock)); |
+ ON_CALL(*password_store_, AddLoginImpl(HasDateSynced(time()))) |
.WillByDefault(Return(PasswordStoreChangeList())); |
ON_CALL(*password_store_, RemoveLoginImpl(_)) |
.WillByDefault(Return(PasswordStoreChangeList())); |
- ON_CALL(*password_store_, UpdateLoginImpl(HasDateSynced())) |
+ ON_CALL(*password_store_, UpdateLoginImpl(HasDateSynced(time()))) |
.WillByDefault(Return(PasswordStoreChangeList())); |
EXPECT_CALL(*password_store(), NotifyLoginsChanged(_)).Times(AnyNumber()); |
} |
@@ -200,7 +202,9 @@ class PasswordSyncableServiceWrapper { |
MockPasswordStore* password_store() { return password_store_.get(); } |
- MockPasswordSyncableService* service() { return service_.get(); } |
+ PasswordSyncableService* service() { return service_.get(); } |
+ |
+ static base::Time time() { return base::Time::FromInternalValue(100000); } |
// Returnes the scoped_ptr to |service_| thus NULLing out it. |
std::unique_ptr<syncer::SyncChangeProcessor> ReleaseSyncableService() { |
@@ -209,7 +213,7 @@ class PasswordSyncableServiceWrapper { |
private: |
scoped_refptr<MockPasswordStore> password_store_; |
- std::unique_ptr<MockPasswordSyncableService> service_; |
+ std::unique_ptr<PasswordSyncableService> service_; |
DISALLOW_COPY_AND_ASSIGN(PasswordSyncableServiceWrapper); |
}; |
@@ -222,13 +226,15 @@ class PasswordSyncableServiceTest : public testing::Test { |
.WillByDefault(Return(SyncError())); |
} |
MockPasswordStore* password_store() { return wrapper_.password_store(); } |
- MockPasswordSyncableService* service() { return wrapper_.service(); } |
+ PasswordSyncableService* service() { return wrapper_.service(); } |
+ |
+ MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType)); |
protected: |
- base::MessageLoop message_loop_; |
std::unique_ptr<MockSyncChangeProcessor> processor_; |
private: |
+ base::test::ScopedTaskEnvironment scoped_task_environment_; |
PasswordSyncableServiceWrapper wrapper_; |
}; |
@@ -488,10 +494,9 @@ TEST_F(PasswordSyncableServiceTest, StartSyncFlare) { |
service()->ActOnPasswordStoreChanges(list); |
// Set the flare. It should be called as there is no SyncChangeProcessor. |
- service()->InjectStartSyncFlare( |
- base::Bind(&MockPasswordSyncableService::StartSyncFlare, |
- base::Unretained(service()))); |
- EXPECT_CALL(*service(), StartSyncFlare(syncer::PASSWORDS)); |
+ service()->InjectStartSyncFlare(base::Bind( |
+ &PasswordSyncableServiceTest::StartSyncFlare, base::Unretained(this))); |
+ EXPECT_CALL(*this, StartSyncFlare(syncer::PASSWORDS)); |
service()->ActOnPasswordStoreChanges(list); |
} |
@@ -673,6 +678,550 @@ TEST_F(PasswordSyncableServiceTest, SerializeNonEmptyPasswordForm) { |
EXPECT_EQ("https://google.com", specifics.federation_url()); |
} |
+// Tests for Android Autofill credentials. Those are saved in the wrong format |
+// without trailing '/'. Nevertheless, password store should always contain the |
+// correct values. |
+class PasswordSyncableServiceAndroidAutofillTest : public testing::Test { |
+ public: |
+ PasswordSyncableServiceAndroidAutofillTest() = default; |
+ |
+ static PasswordFormData android_incorrect(double creation_time) { |
+ PasswordFormData data = {autofill::PasswordForm::SCHEME_HTML, |
+ kAndroidAutofillRealm, |
+ kAndroidAutofillRealm, |
+ "", |
+ L"", |
+ L"", |
+ L"", |
+ L"username_value_1", |
+ L"11111", |
+ true, |
+ creation_time}; |
+ return data; |
+ } |
+ |
+ static PasswordFormData android_correct(double creation_time) { |
+ PasswordFormData data = {autofill::PasswordForm::SCHEME_HTML, |
+ kAndroidCorrectRealm, |
+ kAndroidCorrectRealm, |
+ "", |
+ L"", |
+ L"", |
+ L"", |
+ L"username_value_1", |
+ L"22222", |
+ true, |
+ creation_time}; |
+ return data; |
+ } |
+ |
+ static PasswordFormData android_incorrect2(double creation_time) { |
+ PasswordFormData data = {autofill::PasswordForm::SCHEME_HTML, |
+ kAndroidAutofillRealm, |
+ kAndroidAutofillRealm, |
+ "", |
+ L"", |
+ L"", |
+ L"", |
+ L"username_value_1", |
+ L"33333", |
+ false, |
+ creation_time}; |
+ return data; |
+ } |
+ |
+ static PasswordFormData android_correct2(double creation_time) { |
+ PasswordFormData data = {autofill::PasswordForm::SCHEME_HTML, |
+ kAndroidCorrectRealm, |
+ kAndroidCorrectRealm, |
+ "", |
+ L"", |
+ L"", |
+ L"", |
+ L"username_value_1", |
+ L"444444", |
+ false, |
+ creation_time}; |
+ return data; |
+ } |
+ |
+ static autofill::PasswordForm FormWithCorrectTag(PasswordFormData data) { |
+ autofill::PasswordForm form = *CreatePasswordFormFromDataForTesting(data); |
+ form.signon_realm = kAndroidCorrectRealm; |
+ form.origin = GURL(kAndroidCorrectRealm); |
+ form.date_synced = PasswordSyncableServiceWrapper::time(); |
+ return form; |
+ } |
+ |
+ static autofill::PasswordForm FormWithAndroidAutofillTag( |
+ PasswordFormData data) { |
+ autofill::PasswordForm form = *CreatePasswordFormFromDataForTesting(data); |
+ form.signon_realm = kAndroidAutofillRealm; |
+ form.origin = GURL(kAndroidAutofillRealm); |
+ form.date_synced = PasswordSyncableServiceWrapper::time(); |
+ return form; |
+ } |
+ |
+ // Transforms |val| into |count| numbers from 1 to |count| inclusive. |
+ static std::vector<unsigned> ExtractTimestamps(unsigned val, unsigned count) { |
+ std::vector<unsigned> result; |
+ for (unsigned i = 0; i < count; ++i) { |
+ result.push_back(val % count + 1); |
+ val /= count; |
+ } |
+ return result; |
+ } |
+ |
+ static testing::Message FormDataMessage(const std::string& prefix, |
+ const PasswordFormData* data) { |
+ testing::Message message; |
+ message << prefix; |
+ if (data) |
+ message << *CreatePasswordFormFromDataForTesting(*data); |
+ else |
+ message << "NULL"; |
+ return message; |
+ } |
+ |
+ protected: |
+ base::test::ScopedTaskEnvironment scoped_task_environment_; |
+}; |
+ |
+TEST_F(PasswordSyncableServiceAndroidAutofillTest, FourWayMerge) { |
+ for (unsigned val = 0; val < 4 * 4 * 4 * 4; ++val) { |
+ // Generate 4 creation timestamps for all the entries. |
+ std::vector<unsigned> dates = ExtractTimestamps(val, 4); |
+ ASSERT_EQ(4u, dates.size()); |
+ const unsigned latest = *std::max_element(dates.begin(), dates.end()); |
+ // Sync correct, Sync Android autofill, local correct, local incorrect. |
+ const PasswordFormData data[4] = { |
+ android_correct(dates[0]), android_incorrect(dates[1]), |
+ android_correct2(dates[2]), android_incorrect2(dates[3])}; |
+ const PasswordFormData* latest_data = |
+ std::find_if(data, data + 4, [latest](const PasswordFormData& data) { |
+ return data.creation_time == latest; |
+ }); |
+ ASSERT_TRUE(latest_data); |
+ std::vector<autofill::PasswordForm> expected_sync_updates; |
+ if (latest_data != &data[0]) |
+ expected_sync_updates.push_back(FormWithCorrectTag(*latest_data)); |
+ if (latest_data != &data[1]) |
+ expected_sync_updates.push_back(FormWithAndroidAutofillTag(*latest_data)); |
+ autofill::PasswordForm local_correct = |
+ *CreatePasswordFormFromDataForTesting(data[2]); |
+ autofill::PasswordForm local_incorrect = |
+ *CreatePasswordFormFromDataForTesting(data[3]); |
+ syncer::SyncData sync_correct = |
+ SyncDataFromPassword(*CreatePasswordFormFromDataForTesting(data[0])); |
+ syncer::SyncData sync_incorrect = |
+ SyncDataFromPassword(*CreatePasswordFormFromDataForTesting(data[1])); |
+ |
+ SCOPED_TRACE(*CreatePasswordFormFromDataForTesting(data[0])); |
+ SCOPED_TRACE(*CreatePasswordFormFromDataForTesting(data[1])); |
+ SCOPED_TRACE(*CreatePasswordFormFromDataForTesting(data[2])); |
+ SCOPED_TRACE(*CreatePasswordFormFromDataForTesting(data[3])); |
+ |
+ for (bool correct_sync_first : {true, false}) { |
+ auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>(); |
+ auto processor = |
+ base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>(); |
+ |
+ std::vector<autofill::PasswordForm> stored_forms = {local_correct, |
+ local_incorrect}; |
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) |
+ .WillOnce(AppendForms(stored_forms)); |
+ EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_)) |
+ .WillOnce(Return(true)); |
+ if (latest_data != &data[2]) { |
+ EXPECT_CALL(*wrapper->password_store(), |
+ UpdateLoginImpl(FormWithCorrectTag(*latest_data))); |
+ } |
+ if (latest_data != &data[3]) { |
+ EXPECT_CALL(*wrapper->password_store(), |
+ UpdateLoginImpl(FormWithAndroidAutofillTag(*latest_data))); |
+ } |
+ |
+ if (expected_sync_updates.size() == 1) { |
+ EXPECT_CALL(*processor, |
+ ProcessSyncChanges(_, ElementsAre(SyncChangeIs( |
+ SyncChange::ACTION_UPDATE, |
+ expected_sync_updates[0])))); |
+ } else { |
+ EXPECT_CALL( |
+ *processor, |
+ ProcessSyncChanges(_, UnorderedElementsAre( |
+ SyncChangeIs(SyncChange::ACTION_UPDATE, |
+ expected_sync_updates[0]), |
+ SyncChangeIs(SyncChange::ACTION_UPDATE, |
+ expected_sync_updates[1])))); |
+ } |
+ |
+ SyncDataList sync_list = {sync_correct, sync_incorrect}; |
+ if (!correct_sync_first) |
+ std::swap(sync_list[0], sync_list[1]); |
+ wrapper->service()->MergeDataAndStartSyncing( |
+ syncer::PASSWORDS, sync_list, std::move(processor), |
+ std::unique_ptr<syncer::SyncErrorFactory>()); |
+ wrapper.reset(); |
+ // Wait til PasswordStore is destroy end therefore all the expectations on |
+ // it are checked. |
+ scoped_task_environment_.RunUntilIdle(); |
+ } |
+ } |
+} |
+ |
+TEST_F(PasswordSyncableServiceAndroidAutofillTest, ThreeWayMerge) { |
+ for (int j = 0; j < 4; ++j) { |
+ // Whether the entry exists: Sync correct, Sync Android autofill, |
+ // local correct, local incorrect. |
+ bool entry_present[4] = {true, true, true, true}; |
+ entry_present[j] = false; |
+ for (unsigned val = 0; val < 3 * 3 * 3; ++val) { |
+ // Generate 3 creation timestamps for all the entries. |
+ std::vector<unsigned> dates = ExtractTimestamps(val, 3); |
+ ASSERT_EQ(3u, dates.size()); |
+ const unsigned latest = *std::max_element(dates.begin(), dates.end()); |
+ |
+ // Sync correct, Sync Android autofill, local correct, local incorrect. |
+ std::vector<std::unique_ptr<PasswordFormData>> data; |
+ int date_index = 0; |
+ data.push_back(entry_present[0] |
+ ? base::MakeUnique<PasswordFormData>( |
+ android_correct(dates[date_index++])) |
+ : nullptr); |
+ data.push_back(entry_present[1] |
+ ? base::MakeUnique<PasswordFormData>( |
+ android_incorrect(dates[date_index++])) |
+ : nullptr); |
+ data.push_back(entry_present[2] |
+ ? base::MakeUnique<PasswordFormData>( |
+ android_correct2(dates[date_index++])) |
+ : nullptr); |
+ data.push_back(entry_present[3] |
+ ? base::MakeUnique<PasswordFormData>( |
+ android_incorrect2(dates[date_index++])) |
+ : nullptr); |
+ |
+ SCOPED_TRACE(val); |
+ SCOPED_TRACE(j); |
+ SCOPED_TRACE(FormDataMessage("data[0]=", data[0].get())); |
+ SCOPED_TRACE(FormDataMessage("data[1]=", data[1].get())); |
+ SCOPED_TRACE(FormDataMessage("data[2]=", data[2].get())); |
+ SCOPED_TRACE(FormDataMessage("data[3]=", data[3].get())); |
+ |
+ const PasswordFormData* latest_data = |
+ std::find_if(data.begin(), data.end(), |
+ [latest](const std::unique_ptr<PasswordFormData>& data) { |
+ return data && data->creation_time == latest; |
+ }) |
+ ->get(); |
+ ASSERT_TRUE(latest_data); |
+ std::vector<std::pair<SyncChange::SyncChangeType, autofill::PasswordForm>> |
+ expected_sync_updates; |
+ for (int i = 0; i < 2; ++i) { |
+ if (latest_data != data[i].get()) { |
+ expected_sync_updates.push_back(std::make_pair( |
+ data[i] ? SyncChange::ACTION_UPDATE : SyncChange::ACTION_ADD, |
+ i == 0 ? FormWithCorrectTag(*latest_data) |
+ : FormWithAndroidAutofillTag(*latest_data))); |
+ } |
+ } |
+ |
+ std::vector<autofill::PasswordForm> stored_forms; |
+ for (int i = 2; i < 4; ++i) { |
+ if (data[i]) |
+ stored_forms.push_back( |
+ *CreatePasswordFormFromDataForTesting(*data[i])); |
+ } |
+ |
+ SyncDataList sync_list; |
+ for (int i = 0; i < 2; ++i) { |
+ if (data[i]) { |
+ sync_list.push_back(SyncDataFromPassword( |
+ *CreatePasswordFormFromDataForTesting(*data[i]))); |
+ } |
+ } |
+ |
+ for (bool swap_sync_list : {false, true}) { |
+ auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>(); |
+ auto processor = |
+ base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>(); |
+ |
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) |
+ .WillOnce(AppendForms(stored_forms)); |
+ EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_)) |
+ .WillOnce(Return(true)); |
+ for (int i = 2; i < 4; ++i) { |
+ if (latest_data != data[i].get()) { |
+ autofill::PasswordForm latest_form = |
+ i == 2 ? FormWithCorrectTag(*latest_data) |
+ : FormWithAndroidAutofillTag(*latest_data); |
+ if (data[i]) { |
+ EXPECT_CALL(*wrapper->password_store(), |
+ UpdateLoginImpl(latest_form)); |
+ } else { |
+ EXPECT_CALL(*wrapper->password_store(), |
+ AddLoginImpl(latest_form)); |
+ } |
+ } |
+ } |
+ |
+ if (expected_sync_updates.size() == 0) { |
+ EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty())); |
+ } else if (expected_sync_updates.size() == 1) { |
+ EXPECT_CALL( |
+ *processor, |
+ ProcessSyncChanges(_, ElementsAre(SyncChangeIs( |
+ expected_sync_updates[0].first, |
+ expected_sync_updates[0].second)))); |
+ } else if (expected_sync_updates.size() == 2) { |
+ EXPECT_CALL( |
+ *processor, |
+ ProcessSyncChanges( |
+ _, UnorderedElementsAre( |
+ SyncChangeIs(expected_sync_updates[0].first, |
+ expected_sync_updates[0].second), |
+ SyncChangeIs(expected_sync_updates[1].first, |
+ expected_sync_updates[1].second)))); |
+ } |
+ |
+ if (swap_sync_list && sync_list.size() == 2) |
+ std::swap(sync_list[0], sync_list[1]); |
+ wrapper->service()->MergeDataAndStartSyncing( |
+ syncer::PASSWORDS, sync_list, std::move(processor), |
+ std::unique_ptr<syncer::SyncErrorFactory>()); |
+ wrapper.reset(); |
+ // Wait til PasswordStore is destroy end therefore all the expectations |
+ // on it are checked. |
+ scoped_task_environment_.RunUntilIdle(); |
+ } |
+ } |
+ } |
+} |
+ |
+TEST_F(PasswordSyncableServiceAndroidAutofillTest, TwoWayServerAndLocalMerge) { |
+ for (unsigned i = 0; i < 2 * 2; ++i) { |
+ // Generate 4 different combinations for local/server entries. |
+ std::vector<unsigned> combination = ExtractTimestamps(i, 2); |
+ ASSERT_EQ(2u, combination.size()); |
+ const bool sync_data_correct = !!combination[0]; |
+ const bool local_data_correct = !!combination[1]; |
+ |
+ for (unsigned val = 0; val < 2 * 2; ++val) { |
+ std::vector<unsigned> dates = ExtractTimestamps(i, 2); |
+ ASSERT_EQ(2u, dates.size()); |
+ |
+ const PasswordFormData sync_data = sync_data_correct |
+ ? android_correct(dates[0]) |
+ : android_incorrect(dates[0]); |
+ const PasswordFormData local_data = local_data_correct |
+ ? android_correct2(dates[1]) |
+ : android_incorrect2(dates[1]); |
+ |
+ const PasswordFormData* latest_data = |
+ dates[1] > dates[0] ? &local_data : &sync_data; |
+ |
+ auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>(); |
+ auto processor = |
+ base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>(); |
+ |
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) |
+ .WillOnce( |
+ AppendForm(*CreatePasswordFormFromDataForTesting(local_data))); |
+ EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_)) |
+ .WillOnce(Return(true)); |
+ if (!local_data_correct || latest_data == &sync_data) { |
+ if (local_data_correct) { |
+ EXPECT_CALL(*wrapper->password_store(), |
+ UpdateLoginImpl(FormWithCorrectTag(*latest_data))); |
+ } else { |
+ EXPECT_CALL(*wrapper->password_store(), |
+ AddLoginImpl(FormWithCorrectTag(*latest_data))); |
+ } |
+ } |
+ if (!local_data_correct && latest_data == &sync_data) { |
+ EXPECT_CALL(*wrapper->password_store(), |
+ UpdateLoginImpl(FormWithAndroidAutofillTag(*latest_data))); |
+ } else if (local_data_correct && !sync_data_correct) { |
+ EXPECT_CALL(*wrapper->password_store(), |
+ AddLoginImpl(FormWithAndroidAutofillTag(*latest_data))); |
+ } |
+ |
+ std::vector<std::pair<SyncChange::SyncChangeType, autofill::PasswordForm>> |
+ expected_sync_updates; |
+ // Deal with the correct sync entry and incorrect one. |
+ if (sync_data_correct) { |
+ if (latest_data != &sync_data) { |
+ expected_sync_updates.push_back(std::make_pair( |
+ SyncChange::ACTION_UPDATE, FormWithCorrectTag(*latest_data))); |
+ } |
+ if (!local_data_correct) { |
+ expected_sync_updates.push_back( |
+ std::make_pair(SyncChange::ACTION_ADD, |
+ FormWithAndroidAutofillTag(*latest_data))); |
+ } |
+ } else { |
+ expected_sync_updates.push_back(std::make_pair( |
+ SyncChange::ACTION_ADD, FormWithCorrectTag(*latest_data))); |
+ if (latest_data != &sync_data) { |
+ expected_sync_updates.push_back( |
+ std::make_pair(SyncChange::ACTION_UPDATE, |
+ FormWithAndroidAutofillTag(*latest_data))); |
+ } |
+ } |
+ |
+ // Set expectation on |processor|. |
+ if (expected_sync_updates.size() == 0) { |
+ EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty())); |
+ } else if (expected_sync_updates.size() == 1) { |
+ EXPECT_CALL( |
+ *processor, |
+ ProcessSyncChanges( |
+ _, ElementsAre(SyncChangeIs(expected_sync_updates[0].first, |
+ expected_sync_updates[0].second)))); |
+ } else if (expected_sync_updates.size() == 2) { |
+ EXPECT_CALL(*processor, |
+ ProcessSyncChanges( |
+ _, UnorderedElementsAre( |
+ SyncChangeIs(expected_sync_updates[0].first, |
+ expected_sync_updates[0].second), |
+ SyncChangeIs(expected_sync_updates[1].first, |
+ expected_sync_updates[1].second)))); |
+ } |
+ |
+ SyncDataList sync_list = {SyncDataFromPassword( |
+ *CreatePasswordFormFromDataForTesting(sync_data))}; |
+ wrapper->service()->MergeDataAndStartSyncing( |
+ syncer::PASSWORDS, sync_list, std::move(processor), |
+ std::unique_ptr<syncer::SyncErrorFactory>()); |
+ wrapper.reset(); |
+ // Wait til PasswordStore is destroy end therefore all the expectations on |
+ // it are checked. |
+ scoped_task_environment_.RunUntilIdle(); |
+ } |
+ } |
+} |
+ |
+TEST_F(PasswordSyncableServiceAndroidAutofillTest, OneEntryOnly) { |
+ for (int i = 0; i < 3; ++i) { |
+ // The case when only local incorrect entry exists is excluded. It's very |
+ // exotic because a local incorrect entry can come only from the server. |
+ // In such a case a copy will be uploaded to the server and next |
+ // MergeDataAndStartSyncing will do a proper migration. |
+ SCOPED_TRACE(i); |
+ // Whether the entry exists: Sync correct, Sync Android autofill, |
+ // local correct, local incorrect. |
+ const bool entry_is_correct = i == 0 || i == 2; |
+ const bool entry_is_local = i >= 2; |
+ PasswordFormData data = |
+ entry_is_correct ? android_correct(100) : android_incorrect(100); |
+ |
+ auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>(); |
+ auto processor = |
+ base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>(); |
+ |
+ if (entry_is_local) { |
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) |
+ .WillOnce(AppendForm(*CreatePasswordFormFromDataForTesting(data))); |
+ } else { |
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) |
+ .WillOnce(Return(true)); |
+ } |
+ EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_)) |
+ .WillOnce(Return(true)); |
+ if (!entry_is_local && !entry_is_correct) { |
+ EXPECT_CALL(*wrapper->password_store(), |
+ AddLoginImpl(FormWithAndroidAutofillTag(data))); |
+ } |
+ if (!entry_is_local) { |
+ EXPECT_CALL(*wrapper->password_store(), |
+ AddLoginImpl(FormWithCorrectTag(data))); |
+ } |
+ |
+ if (entry_is_correct && !entry_is_local) { |
+ EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty())); |
+ } else { |
+ EXPECT_CALL(*processor, |
+ ProcessSyncChanges( |
+ _, ElementsAre(SyncChangeIs(SyncChange::ACTION_ADD, |
+ FormWithCorrectTag(data))))); |
+ } |
+ |
+ SyncDataList sync_list; |
+ if (!entry_is_local) { |
+ sync_list.push_back( |
+ SyncDataFromPassword(*CreatePasswordFormFromDataForTesting(data))); |
+ } |
+ wrapper->service()->MergeDataAndStartSyncing( |
+ syncer::PASSWORDS, sync_list, std::move(processor), |
+ std::unique_ptr<syncer::SyncErrorFactory>()); |
+ wrapper.reset(); |
+ // Wait til PasswordStore is destroy end therefore all the expectations on |
+ // it are checked. |
+ scoped_task_environment_.RunUntilIdle(); |
+ } |
+} |
+ |
+TEST_F(PasswordSyncableServiceAndroidAutofillTest, FourEqualEntries) { |
+ // Sync correct, Sync Android autofill, local correct, local incorrect with |
+ // the same content. Nothing should happen. |
+ const PasswordFormData data = android_correct(100); |
+ autofill::PasswordForm local_correct = FormWithCorrectTag(data); |
+ autofill::PasswordForm local_incorrect = FormWithAndroidAutofillTag(data); |
+ syncer::SyncData sync_correct = SyncDataFromPassword(local_correct); |
+ syncer::SyncData sync_incorrect = SyncDataFromPassword(local_incorrect); |
+ |
+ for (bool correct_sync_first : {true, false}) { |
+ auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>(); |
+ auto processor = |
+ base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>(); |
+ |
+ std::vector<autofill::PasswordForm> stored_forms = {local_correct, |
+ local_incorrect}; |
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) |
+ .WillOnce(AppendForms(stored_forms)); |
+ EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_)) |
+ .WillOnce(Return(true)); |
+ EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty())); |
+ |
+ SyncDataList sync_list = {sync_correct, sync_incorrect}; |
+ if (!correct_sync_first) |
+ std::swap(sync_list[0], sync_list[1]); |
+ wrapper->service()->MergeDataAndStartSyncing( |
+ syncer::PASSWORDS, sync_list, std::move(processor), |
+ std::unique_ptr<syncer::SyncErrorFactory>()); |
+ wrapper.reset(); |
+ // Wait til PasswordStore is destroy end therefore all the expectations on |
+ // it are checked. |
+ scoped_task_environment_.RunUntilIdle(); |
+ } |
+} |
+ |
+TEST_F(PasswordSyncableServiceAndroidAutofillTest, AndroidCorrectEqualEntries) { |
+ // Sync correct, local correct with the same content. Nothing should happen. |
+ const PasswordFormData data = android_correct(100); |
+ autofill::PasswordForm local_correct = FormWithCorrectTag(data); |
+ syncer::SyncData sync_correct = SyncDataFromPassword(local_correct); |
+ |
+ auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>(); |
+ auto processor = |
+ base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>(); |
+ |
+ EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) |
+ .WillOnce(AppendForm(local_correct)); |
+ EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_)) |
+ .WillOnce(Return(true)); |
+ EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty())); |
+ |
+ wrapper->service()->MergeDataAndStartSyncing( |
+ syncer::PASSWORDS, {sync_correct}, std::move(processor), |
+ std::unique_ptr<syncer::SyncErrorFactory>()); |
+ wrapper.reset(); |
+ // Wait til PasswordStore is destroy end therefore all the expectations on |
+ // it are checked. |
+ scoped_task_environment_.RunUntilIdle(); |
+} |
+ |
} // namespace |
} // namespace password_manager |