Chromium Code Reviews| 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..4ee3608e276ae21b02a2cb174908d84e4f47c49d 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 0 to |count|. |
|
engedy
2017/07/20 19:13:07
nit: 1 to |count|, inclusive.
vasilii
2017/07/20 19:54:46
Done.
|
| + 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 |