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

Unified Diff: components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc

Issue 2620783002: [sync] Handle local changes in AutocompleteSyncBridge (Closed)
Patch Set: self-review cleanups Created 3 years, 11 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
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..b4a519c33633e59895cbc4b1211aedbf1875077d 100644
--- a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
@@ -14,16 +14,23 @@
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
+#include "components/autofill/core/browser/webdata/autofill_entry.h"
#include "components/autofill/core/browser/webdata/autofill_table.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
-#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h"
#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/recording_model_type_change_processor.h"
#include "components/webdata/common/web_database.h"
#include "testing/gtest/include/gtest/gtest.h"
+using base::ASCIIToUTF16;
+using base::Time;
+using base::TimeDelta;
using sync_pb::AutofillSpecifics;
using sync_pb::EntitySpecifics;
using syncer::EntityDataPtr;
@@ -54,13 +61,7 @@ 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>();
-}
-
-AutofillEntry CreateAutofillEntry(
+AutofillEntry CreateAutofillEntryFromSpecifics(
const sync_pb::AutofillSpecifics& autofill_specifics) {
AutofillKey key(base::UTF8ToUTF16(autofill_specifics.name()),
base::UTF8ToUTF16(autofill_specifics.value()));
@@ -74,7 +75,29 @@ AutofillEntry CreateAutofillEntry(
return AutofillEntry(key, date_created, date_last_used);
}
-// Creates an EntityData/EntityDataPtr around a copy of the given specifics.
+AutofillEntry CreateAutofillEntryFromValues(const char* name,
+ const char* value,
+ int time_shift0,
+ int time_shift1) {
+ // Time deep in the past would cause Autocomplete sync to discard the
+ // entries.
+ static Time base_time = Time::Now().LocalMidnight();
+
+ Time date_created = base_time + TimeDelta::FromSeconds(time_shift0);
+ Time date_last_used = date_created;
+ if (time_shift1 >= 0) {
+ date_last_used = base_time + TimeDelta::FromSeconds(time_shift1);
+ }
+ return AutofillEntry(AutofillKey(ASCIIToUTF16(name), ASCIIToUTF16(value)),
+ date_created, date_last_used);
+}
+
+AutofillEntry CreateAutofillEntryFromValues(const char* name,
+ const char* value,
+ int time_shift) {
+ return CreateAutofillEntryFromValues(name, value, time_shift, -1);
skym 2017/01/12 00:18:32 I really dislike this -1 approach.
skym 2017/01/12 00:40:29 Like, what do you think of maybe just passing time
Patrick Noland 2017/01/13 23:31:52 Done.
+}
+
EntityDataPtr SpecificsToEntity(const AutofillSpecifics& specifics) {
EntityData data;
data.client_tag_hash = "ignored";
@@ -82,6 +105,8 @@ EntityDataPtr SpecificsToEntity(const AutofillSpecifics& specifics) {
return data.PassToPtr();
}
+} // namespace
maxbogue 2017/01/12 01:33:36 Why'd you take the FakeAutofillBackend out of the
Patrick Noland 2017/01/13 23:31:52 Done.
+
class FakeAutofillBackend : public AutofillWebDataBackend {
public:
FakeAutofillBackend() {}
@@ -100,30 +125,31 @@ class FakeAutofillBackend : public AutofillWebDataBackend {
WebDatabase* db_;
};
-} // namespace
-
class AutocompleteSyncBridgeTest : public testing::Test {
- public:
- AutocompleteSyncBridgeTest() {
- if (temp_dir_.CreateUniqueTempDir()) {
- db_.AddTable(&table_);
- db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
- backend_.SetWebDatabase(&db_);
-
- bridge_.reset(new AutocompleteSyncBridge(
- &backend_, base::Bind(&CreateModelTypeChangeProcessor)));
- }
- }
- ~AutocompleteSyncBridgeTest() override {}
-
protected:
maxbogue 2017/01/12 01:33:36 I don't understand the protected thing in tests. J
Patrick Noland 2017/01/13 23:31:52 Done.
+ // A non-owning pointer to the processor given to the bridge. Will be null
+ // before being given to the bridge, to make ownership easier.
+ syncer::RecordingModelTypeChangeProcessor* processor_ = nullptr;
maxbogue 2017/01/12 01:33:36 This variable has an accessor, it should just be p
Patrick Noland 2017/01/13 23:31:52 Done.
+ AutofillTable table_;
maxbogue 2017/01/12 01:33:36 Give this variable an accessor and make it private
Patrick Noland 2017/01/13 23:31:52 Done.
+
AutocompleteSyncBridge* bridge() { return bridge_.get(); }
+ syncer::RecordingModelTypeChangeProcessor* processor() { return processor_; }
+
+ std::unique_ptr<syncer::ModelTypeChangeProcessor>
+ CreateModelTypeChangeProcessor(syncer::ModelType type,
+ syncer::ModelTypeSyncBridge* bridge) {
+ auto processor =
+ base::MakeUnique<syncer::RecordingModelTypeChangeProcessor>();
+ processor_ = processor.get();
+ return std::move(processor);
+ }
+
void SaveSpecificsToTable(
const std::vector<AutofillSpecifics>& specifics_list) {
std::vector<AutofillEntry> new_entries;
for (const auto& specifics : specifics_list) {
- new_entries.push_back(CreateAutofillEntry(specifics));
+ new_entries.push_back(CreateAutofillEntryFromSpecifics(specifics));
}
table_.UpdateAutofillEntries(new_entries);
}
@@ -143,15 +169,34 @@ class AutocompleteSyncBridgeTest : public testing::Test {
return key;
}
+ std::string GetStorageKeyFromModel(const AutofillKey& key) {
+ return bridge()->GetStorageKeyFromModel(key);
+ }
+
private:
base::ScopedTempDir temp_dir_;
base::MessageLoop message_loop_;
FakeAutofillBackend backend_;
- AutofillTable table_;
WebDatabase db_;
std::unique_ptr<AutocompleteSyncBridge> bridge_;
DISALLOW_COPY_AND_ASSIGN(AutocompleteSyncBridgeTest);
+
+ public:
maxbogue 2017/01/12 01:33:36 plz put public section above the protected one for
Patrick Noland 2017/01/13 23:31:52 Done.
+ AutocompleteSyncBridgeTest() {
+ if (temp_dir_.CreateUniqueTempDir()) {
+ db_.AddTable(&table_);
+ db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
+ backend_.SetWebDatabase(&db_);
+
+ bridge_.reset(new AutocompleteSyncBridge(
+ &backend_,
+ base::Bind(
+ &AutocompleteSyncBridgeTest::CreateModelTypeChangeProcessor,
+ base::Unretained(this))));
+ }
+ }
+ ~AutocompleteSyncBridgeTest() override {}
};
TEST_F(AutocompleteSyncBridgeTest, GetClientTag) {
@@ -244,4 +289,122 @@ TEST_F(AutocompleteSyncBridgeTest, GetAllData) {
bridge()->GetAllData(base::Bind(&VerifyDataBatch, expected));
}
-} // namespace autofill
+TEST_F(AutocompleteSyncBridgeTest, LocalEntriesAdded) {
+ const AutofillEntry added_entry1(
+ CreateAutofillEntryFromValues("added", "entry1", 1));
+ const AutofillEntry added_entry2(
+ CreateAutofillEntryFromValues("added", "entry2", 1));
+
+ table_.UpdateAutofillEntries({added_entry1, added_entry2});
+
+ const AutofillKey entry_key1 = added_entry1.key();
+ const AutofillKey entry_key2 = added_entry2.key();
+
+ bridge()->AutofillEntriesChanged(
+ {AutofillChange(AutofillChange::ADD, entry_key1),
+ AutofillChange(AutofillChange::ADD, entry_key2)});
+
+ EXPECT_EQ(2u, processor()->put_multimap().size());
+
+ const auto& recorded_specifics_for_key1 =
+ processor()->put_multimap().find(GetStorageKeyFromModel(entry_key1));
skym 2017/01/12 00:18:32 What do you think of creating the model objects, m
Patrick Noland 2017/01/13 23:31:52 Done. I've added a method that converts from an Au
+ EXPECT_NE(processor()->put_multimap().end(), recorded_specifics_for_key1);
+ sync_pb::AutofillSpecifics specifics =
+ recorded_specifics_for_key1->second->specifics.autofill();
+ EXPECT_EQ(specifics.value(), "entry1");
+
+ const auto& recorded_specifics_for_key2 =
+ processor()->put_multimap().find(GetStorageKeyFromModel(entry_key2));
+ EXPECT_NE(processor()->put_multimap().end(), recorded_specifics_for_key2);
+ specifics = recorded_specifics_for_key2->second->specifics.autofill();
+ EXPECT_EQ(specifics.value(), "entry2");
+}
+
+TEST_F(AutocompleteSyncBridgeTest, LocalEntryAddedThenUpdated) {
+ AutofillEntry added_entry(CreateAutofillEntryFromValues("my", "entry", 1));
+ table_.UpdateAutofillEntries({added_entry});
+ AutofillKey entry_key = added_entry.key();
+
+ bridge()->AutofillEntriesChanged(
+ {AutofillChange(AutofillChange::ADD, entry_key)});
+
+ EXPECT_EQ(1u, processor()->put_multimap().size());
+
+ const std::string storage_key = GetStorageKeyFromModel(entry_key);
+ auto recorded_specifics_for_key =
+ processor()->put_multimap().find(storage_key);
+ EXPECT_NE(processor()->put_multimap().end(), recorded_specifics_for_key);
+
+ sync_pb::AutofillSpecifics specifics =
+ recorded_specifics_for_key->second->specifics.autofill();
+ EXPECT_EQ(added_entry.date_last_used(),
+ Time::FromInternalValue(specifics.usage_timestamp(0)));
+
+ AutofillEntry updated_entry(
+ CreateAutofillEntryFromValues("my", "entry", 1, 4));
+ table_.UpdateAutofillEntries({updated_entry});
+ AutofillKey updated_key = updated_entry.key();
+
+ bridge()->AutofillEntriesChanged(
+ {AutofillChange(AutofillChange::UPDATE, entry_key)});
+
+ // RecordingModelTypeChangeProcessor's put_multimap records each put for a
+ // given key in an ordered list, so we need to get the iterator for this list
+ // and then advance it to find the update we just applied.
+ recorded_specifics_for_key = processor()->put_multimap().find(storage_key);
+ EXPECT_NE(processor()->put_multimap().end(), recorded_specifics_for_key);
+ recorded_specifics_for_key++;
+ EXPECT_NE(processor()->put_multimap().end(), recorded_specifics_for_key);
+
+ specifics = recorded_specifics_for_key->second->specifics.autofill();
+ EXPECT_EQ(2, specifics.usage_timestamp_size());
+ EXPECT_EQ(updated_entry.date_last_used(),
+ Time::FromInternalValue(specifics.usage_timestamp(1)));
+}
+
+TEST_F(AutocompleteSyncBridgeTest, LocalEntryDeleted) {
+ AutofillEntry deleted_entry(CreateAutofillEntryFromValues("my", "entry", 1));
+
+ AutofillKey deleted_key = deleted_entry.key();
+ const std::string storage_key = GetStorageKeyFromModel(deleted_key);
+
+ bridge()->AutofillEntriesChanged(
+ {AutofillChange(AutofillChange::REMOVE, deleted_key)});
+
+ EXPECT_EQ(1u, processor()->delete_set().size());
+ const auto& deleted_entries = processor()->delete_set().find(storage_key);
+ EXPECT_NE(deleted_entries, processor()->delete_set().end());
+}
+
+// Helper struct to track callback invocation status.
+struct TestForRun {
skym 2017/01/12 00:18:32 Should this get moved to up above the test cases?
maxbogue 2017/01/12 01:33:36 (yes, inside the anonymous namespace)
Patrick Noland 2017/01/13 23:31:52 n/a now that flare is gone
+ TestForRun() : cb(base::Bind(&TestForRun::Run, base::Unretained(this))) {}
+
+ void Run(syncer::ModelType type) { cb_run = true; }
+
+ bool cb_run;
+ AutocompleteSyncBridge::StartSyncFlare cb;
+};
+
+TEST_F(AutocompleteSyncBridgeTest, TestSyncFlare) {
+ processor()->SetIsTrackingMetadata(false);
+
+ TestForRun tfr;
+ AutocompleteSyncBridge::StartSyncFlare flare = tfr.cb;
+
+ bridge()->InjectStartSyncFlare(flare);
+
+ AutofillEntry deleted_entry(CreateAutofillEntryFromValues("my", "entry", 1));
+
+ AutofillKey deleted_key = deleted_entry.key();
+ const std::string storage_key = GetStorageKeyFromModel(deleted_key);
+
+ bridge()->AutofillEntriesChanged(
+ {AutofillChange(AutofillChange::REMOVE, deleted_key)});
+
+ // The processor shouldn't be called if it's not yet tracking metadata.
+ EXPECT_EQ(0u, processor()->delete_set().size());
+ EXPECT_TRUE(tfr.cb_run);
+}
+
+}; // namespace autofill
maxbogue 2017/01/12 01:33:36 No semi-colon, that's just for classes because C++
Patrick Noland 2017/01/13 23:31:52 Done.

Powered by Google App Engine
This is Rietveld 408576698