| Index: components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
|
| diff --git a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
|
| index 4e7fd4232e3583ed5ffdbf0d409ff5b7be967d21..515b874815c6918f967a21bb325e5954b0a078c2 100644
|
| --- a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
|
| +++ b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
|
| @@ -4,6 +4,7 @@
|
|
|
| #include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
|
|
|
| +#include <algorithm>
|
| #include <map>
|
| #include <vector>
|
|
|
| @@ -21,13 +22,25 @@
|
| #include "components/sync/model/data_batch.h"
|
| #include "components/sync/model/fake_model_type_change_processor.h"
|
| #include "components/sync/model/metadata_batch.h"
|
| +#include "components/sync/model/model_error.h"
|
| #include "components/webdata/common/web_database.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| +using base::ScopedTempDir;
|
| +using base::Time;
|
| using sync_pb::AutofillSpecifics;
|
| using sync_pb::EntitySpecifics;
|
| -using syncer::EntityDataPtr;
|
| +using syncer::DataBatch;
|
| using syncer::EntityData;
|
| +using syncer::EntityDataPtr;
|
| +using syncer::EntityChange;
|
| +using syncer::EntityChangeList;
|
| +using syncer::FakeModelTypeChangeProcessor;
|
| +using syncer::KeyAndData;
|
| +using syncer::ModelError;
|
| +using syncer::ModelType;
|
| +using syncer::ModelTypeChangeProcessor;
|
| +using syncer::ModelTypeSyncBridge;
|
|
|
| namespace autofill {
|
|
|
| @@ -37,13 +50,25 @@ const char kNameFormat[] = "name %d";
|
| const char kValueFormat[] = "value %d";
|
|
|
| void VerifyEqual(const AutofillSpecifics& s1, const AutofillSpecifics& s2) {
|
| - EXPECT_EQ(s1.SerializeAsString(), s2.SerializeAsString());
|
| + // Instead of just comparing serialized strings, manually check fields to show
|
| + // differences on failure.
|
| + EXPECT_EQ(s1.name(), s2.name());
|
| + EXPECT_EQ(s1.value(), s2.value());
|
| + EXPECT_EQ(s1.usage_timestamp().size(), s2.usage_timestamp().size());
|
| + int size = std::min(s1.usage_timestamp().size(), s2.usage_timestamp().size());
|
| + for (int i = 0; i < size; ++i) {
|
| + EXPECT_EQ(s1.usage_timestamp(i), s2.usage_timestamp(i))
|
| + << "Values differ at index " << i;
|
| + }
|
| + // Neither should have any profile data, so we don't have to compare it.
|
| + EXPECT_FALSE(s1.has_profile());
|
| + EXPECT_FALSE(s2.has_profile());
|
| }
|
|
|
| void VerifyDataBatch(std::map<std::string, AutofillSpecifics> expected,
|
| - std::unique_ptr<syncer::DataBatch> batch) {
|
| + std::unique_ptr<DataBatch> batch) {
|
| while (batch->HasNext()) {
|
| - const syncer::KeyAndData& pair = batch->Next();
|
| + const KeyAndData& pair = batch->Next();
|
| auto iter = expected.find(pair.first);
|
| ASSERT_NE(iter, expected.end());
|
| VerifyEqual(iter->second, pair.second->specifics.autofill());
|
| @@ -54,22 +79,22 @@ void VerifyDataBatch(std::map<std::string, AutofillSpecifics> expected,
|
| EXPECT_TRUE(expected.empty());
|
| }
|
|
|
| -std::unique_ptr<syncer::ModelTypeChangeProcessor>
|
| -CreateModelTypeChangeProcessor(syncer::ModelType type,
|
| - syncer::ModelTypeSyncBridge* bridge) {
|
| - return base::MakeUnique<syncer::FakeModelTypeChangeProcessor>();
|
| +std::unique_ptr<ModelTypeChangeProcessor> CreateModelTypeChangeProcessor(
|
| + ModelType type,
|
| + ModelTypeSyncBridge* bridge) {
|
| + return base::MakeUnique<FakeModelTypeChangeProcessor>();
|
| }
|
|
|
| AutofillEntry CreateAutofillEntry(
|
| const sync_pb::AutofillSpecifics& autofill_specifics) {
|
| AutofillKey key(base::UTF8ToUTF16(autofill_specifics.name()),
|
| base::UTF8ToUTF16(autofill_specifics.value()));
|
| - base::Time date_created, date_last_used;
|
| + Time date_created, date_last_used;
|
| const google::protobuf::RepeatedField<int64_t>& timestamps =
|
| autofill_specifics.usage_timestamp();
|
| if (!timestamps.empty()) {
|
| - date_created = base::Time::FromInternalValue(*timestamps.begin());
|
| - date_last_used = base::Time::FromInternalValue(*timestamps.rbegin());
|
| + date_created = Time::FromInternalValue(*timestamps.begin());
|
| + date_last_used = Time::FromInternalValue(*timestamps.rbegin());
|
| }
|
| return AutofillEntry(key, date_created, date_last_used);
|
| }
|
| @@ -93,7 +118,7 @@ class FakeAutofillBackend : public AutofillWebDataBackend {
|
| autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {}
|
| void RemoveExpiredFormElements() override {}
|
| void NotifyOfMultipleAutofillChanges() override {}
|
| - void NotifyThatSyncHasStarted(syncer::ModelType model_type) override {}
|
| + void NotifyThatSyncHasStarted(ModelType model_type) override {}
|
| void SetWebDatabase(WebDatabase* db) { db_ = db; }
|
|
|
| private:
|
| @@ -128,14 +153,30 @@ class AutocompleteSyncBridgeTest : public testing::Test {
|
| table_.UpdateAutofillEntries(new_entries);
|
| }
|
|
|
| - AutofillSpecifics CreateSpecifics(int suffix) {
|
| + AutofillSpecifics CreateSpecifics(const std::string& name,
|
| + const std::string& value,
|
| + const std::vector<int>& timestamps) {
|
| AutofillSpecifics specifics;
|
| - specifics.set_name(base::StringPrintf(kNameFormat, suffix));
|
| - specifics.set_value(base::StringPrintf(kValueFormat, suffix));
|
| - specifics.add_usage_timestamp(0);
|
| + specifics.set_name(name);
|
| + specifics.set_value(value);
|
| + for (int timestamp : timestamps) {
|
| + specifics.add_usage_timestamp(
|
| + Time::FromTimeT(timestamp).ToInternalValue());
|
| + }
|
| return specifics;
|
| }
|
|
|
| + AutofillSpecifics CreateSpecifics(int suffix,
|
| + const std::vector<int>& timestamps) {
|
| + return CreateSpecifics(base::StringPrintf(kNameFormat, suffix),
|
| + base::StringPrintf(kValueFormat, suffix),
|
| + timestamps);
|
| + }
|
| +
|
| + AutofillSpecifics CreateSpecifics(int suffix) {
|
| + return CreateSpecifics(suffix, std::vector<int>{0});
|
| + }
|
| +
|
| std::string GetStorageKey(const AutofillSpecifics& specifics) {
|
| std::string key =
|
| bridge()->GetStorageKey(SpecificsToEntity(specifics).value());
|
| @@ -143,8 +184,41 @@ class AutocompleteSyncBridgeTest : public testing::Test {
|
| return key;
|
| }
|
|
|
| + EntityChangeList EntityAddList(
|
| + const std::vector<AutofillSpecifics>& specifics_vector) {
|
| + EntityChangeList changes;
|
| + for (const auto& specifics : specifics_vector) {
|
| + changes.push_back(EntityChange::CreateAdd(GetStorageKey(specifics),
|
| + SpecificsToEntity(specifics)));
|
| + }
|
| + return changes;
|
| + }
|
| +
|
| + void VerifyApplyChanges(const std::vector<EntityChange>& changes) {
|
| + const auto error = bridge()->ApplySyncChanges(
|
| + bridge()->CreateMetadataChangeList(), changes);
|
| + EXPECT_FALSE(error);
|
| + }
|
| +
|
| + void VerifyApplyAdds(const std::vector<AutofillSpecifics>& specifics) {
|
| + VerifyApplyChanges(EntityAddList(specifics));
|
| + }
|
| +
|
| + std::map<std::string, AutofillSpecifics> ExpectedMap(
|
| + const std::vector<AutofillSpecifics>& specifics_vector) {
|
| + std::map<std::string, AutofillSpecifics> map;
|
| + for (const auto& specifics : specifics_vector) {
|
| + map[GetStorageKey(specifics)] = specifics;
|
| + }
|
| + return map;
|
| + }
|
| +
|
| + void VerifyAllData(const std::vector<AutofillSpecifics>& expected) {
|
| + bridge()->GetAllData(base::Bind(&VerifyDataBatch, ExpectedMap(expected)));
|
| + }
|
| +
|
| private:
|
| - base::ScopedTempDir temp_dir_;
|
| + ScopedTempDir temp_dir_;
|
| base::MessageLoop message_loop_;
|
| FakeAutofillBackend backend_;
|
| AutofillTable table_;
|
| @@ -209,12 +283,9 @@ TEST_F(AutocompleteSyncBridgeTest, GetData) {
|
| const AutofillSpecifics specifics2 = CreateSpecifics(2);
|
| const AutofillSpecifics specifics3 = CreateSpecifics(3);
|
| SaveSpecificsToTable({specifics1, specifics2, specifics3});
|
| -
|
| - const std::map<std::string, AutofillSpecifics> expected{
|
| - {GetStorageKey(specifics1), specifics1},
|
| - {GetStorageKey(specifics3), specifics3}};
|
| - bridge()->GetData({GetStorageKey(specifics1), GetStorageKey(specifics3)},
|
| - base::Bind(&VerifyDataBatch, expected));
|
| + bridge()->GetData(
|
| + {GetStorageKey(specifics1), GetStorageKey(specifics3)},
|
| + base::Bind(&VerifyDataBatch, ExpectedMap({specifics1, specifics3})));
|
| }
|
|
|
| TEST_F(AutocompleteSyncBridgeTest, GetDataNotExist) {
|
| @@ -222,13 +293,10 @@ TEST_F(AutocompleteSyncBridgeTest, GetDataNotExist) {
|
| const AutofillSpecifics specifics2 = CreateSpecifics(2);
|
| const AutofillSpecifics specifics3 = CreateSpecifics(3);
|
| SaveSpecificsToTable({specifics1, specifics2});
|
| -
|
| - const std::map<std::string, AutofillSpecifics> expected{
|
| - {GetStorageKey(specifics1), specifics1},
|
| - {GetStorageKey(specifics2), specifics2}};
|
| - bridge()->GetData({GetStorageKey(specifics1), GetStorageKey(specifics2),
|
| - GetStorageKey(specifics3)},
|
| - base::Bind(&VerifyDataBatch, expected));
|
| + bridge()->GetData(
|
| + {GetStorageKey(specifics1), GetStorageKey(specifics2),
|
| + GetStorageKey(specifics3)},
|
| + base::Bind(&VerifyDataBatch, ExpectedMap({specifics1, specifics2})));
|
| }
|
|
|
| TEST_F(AutocompleteSyncBridgeTest, GetAllData) {
|
| @@ -236,12 +304,126 @@ TEST_F(AutocompleteSyncBridgeTest, GetAllData) {
|
| const AutofillSpecifics specifics2 = CreateSpecifics(2);
|
| const AutofillSpecifics specifics3 = CreateSpecifics(3);
|
| SaveSpecificsToTable({specifics1, specifics2, specifics3});
|
| + VerifyAllData({specifics1, specifics2, specifics3});
|
| +}
|
| +
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesEmpty) {
|
| + // TODO(skym, crbug.com/672619): Ideally would like to verify that the db is
|
| + // not accessed.
|
| + VerifyApplyAdds(std::vector<AutofillSpecifics>());
|
| +}
|
| +
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesSimple) {
|
| + AutofillSpecifics specifics1 = CreateSpecifics(1);
|
| + AutofillSpecifics specifics2 = CreateSpecifics(2);
|
| + ASSERT_NE(specifics1.SerializeAsString(), specifics2.SerializeAsString());
|
| + ASSERT_NE(GetStorageKey(specifics1), GetStorageKey(specifics2));
|
| +
|
| + VerifyApplyAdds({specifics1, specifics2});
|
| + VerifyAllData({specifics1, specifics2});
|
| +
|
| + VerifyApplyChanges({EntityChange::CreateDelete(GetStorageKey(specifics1))});
|
| + VerifyAllData({specifics2});
|
| +}
|
| +
|
| +// Should be resilient to deleting and updating non-existent things, and adding
|
| +// existing ones.
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesWrongChangeType) {
|
| + AutofillSpecifics specifics = CreateSpecifics(1, {1});
|
| + VerifyApplyChanges({EntityChange::CreateDelete(GetStorageKey(specifics))});
|
| + VerifyAllData(std::vector<AutofillSpecifics>());
|
| +
|
| + VerifyApplyChanges({EntityChange::CreateUpdate(
|
| + GetStorageKey(specifics), SpecificsToEntity(specifics))});
|
| + VerifyAllData({specifics});
|
| +
|
| + specifics.add_usage_timestamp(Time::FromTimeT(2).ToInternalValue());
|
| + VerifyApplyAdds({specifics});
|
| + VerifyAllData({specifics});
|
| +}
|
| +
|
| +// The format in the table has a fixed 2 timestamp format. Round tripping is
|
| +// lossy and the middle value should be thrown out.
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesThreeTimestamps) {
|
| + VerifyApplyAdds({CreateSpecifics(1, {1, 2, 3})});
|
| + VerifyAllData({CreateSpecifics(1, {1, 3})});
|
| +}
|
| +
|
| +// The correct format of timestamps is that the first should be smaller and the
|
| +// second should be larger. Bad foreign data should be repaired.
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesWrongOrder) {
|
| + VerifyApplyAdds({CreateSpecifics(1, {3, 2})});
|
| + VerifyAllData({CreateSpecifics(1, {2, 3})});
|
| +}
|
| +
|
| +// In a minor attempt to save bandwidth, we only send one of the two timestamps
|
| +// when they share a value.
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesRepeatedTime) {
|
| + VerifyApplyAdds({CreateSpecifics(1, {2, 2})});
|
| + VerifyAllData({CreateSpecifics(1, {2})});
|
| +}
|
| +
|
| +// Again, the format in the table is lossy, and cannot distinguish between no
|
| +// time, and valid time zero.
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoTime) {
|
| + VerifyApplyAdds({CreateSpecifics(1, std::vector<int>())});
|
| + VerifyAllData({CreateSpecifics(1, {0})});
|
| +}
|
| +
|
| +// If has_value() returns false, then the specifics are determined to be old
|
| +// style and ignored.
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoValue) {
|
| + AutofillSpecifics input = CreateSpecifics(1, {2, 3});
|
| + input.clear_value();
|
| + VerifyApplyAdds({input});
|
| + VerifyAllData(std::vector<AutofillSpecifics>());
|
| +}
|
| +
|
| +// Should be treated the same as an empty string name. This inconsistency is
|
| +// being perpetuated from the previous sync integration.
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoName) {
|
| + AutofillSpecifics input = CreateSpecifics(1, {2, 3});
|
| + input.clear_name();
|
| + VerifyApplyAdds({input});
|
| + VerifyAllData({input});
|
| +}
|
| +
|
| +// UTF-8 characters should not be dropped when round tripping, including middle
|
| +// of string \0 characters.
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesUTF) {
|
| + const AutofillSpecifics specifics =
|
| + CreateSpecifics(std::string("\n\0\x12\0", 4), "\xEC\xA4\x91", {1});
|
| + VerifyApplyAdds({specifics});
|
| + VerifyAllData({specifics});
|
| +}
|
| +
|
| +// Timestamps should always use the oldest creation time, and the most recent
|
| +// usage time.
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesMinMaxTimestamps) {
|
| + const AutofillSpecifics initial = CreateSpecifics(1, {3, 6});
|
| + VerifyApplyAdds({initial});
|
| + VerifyAllData({initial});
|
| +
|
| + VerifyApplyAdds({CreateSpecifics(1, {2, 5})});
|
| + VerifyAllData({CreateSpecifics(1, {2, 6})});
|
| +
|
| + VerifyApplyAdds({CreateSpecifics(1, {4, 7})});
|
| + VerifyAllData({CreateSpecifics(1, {2, 7})});
|
| +}
|
| +
|
| +// An error should be generated when parsing the storage key happens. This
|
| +// should never happen in practice because storage keys should be generated at
|
| +// runtime by the bridge and not loaded from disk.
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesBadStorageKey) {
|
| + const auto error = bridge()->ApplySyncChanges(
|
| + bridge()->CreateMetadataChangeList(),
|
| + {EntityChange::CreateDelete("bogus storage key")});
|
| + EXPECT_TRUE(error);
|
| +}
|
|
|
| - const std::map<std::string, AutofillSpecifics> expected{
|
| - {GetStorageKey(specifics1), specifics1},
|
| - {GetStorageKey(specifics2), specifics2},
|
| - {GetStorageKey(specifics3), specifics3}};
|
| - bridge()->GetAllData(base::Bind(&VerifyDataBatch, expected));
|
| +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesDatabaseFailure) {
|
| + // TODO(skym, crbug.com/672619): Should have tests that get false back when
|
| + // making db calls and verify the errors are propagated up.
|
| }
|
|
|
| } // namespace autofill
|
|
|