| 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
|
|
|