Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(464)

Unified Diff: components/password_manager/core/browser/password_syncable_service_unittest.cc

Issue 2981293003: Save Android Autofill credentials in the right format. (Closed)
Patch Set: test comment Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « components/password_manager/core/browser/password_syncable_service.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « components/password_manager/core/browser/password_syncable_service.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698