123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/sync_user_events/user_event_sync_bridge.h"
- #include <map>
- #include <set>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/run_loop.h"
- #include "base/test/task_environment.h"
- #include "components/sync/model/data_batch.h"
- #include "components/sync/protocol/entity_specifics.pb.h"
- #include "components/sync/protocol/user_event_specifics.pb.h"
- #include "components/sync/test/mock_model_type_change_processor.h"
- #include "components/sync/test/model_type_store_test_util.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace syncer {
- namespace {
- using sync_pb::UserEventSpecifics;
- using testing::_;
- using testing::ElementsAre;
- using testing::InvokeWithoutArgs;
- using testing::IsEmpty;
- using testing::IsNull;
- using testing::NotNull;
- using testing::Pair;
- using testing::Pointee;
- using testing::Return;
- using testing::SaveArg;
- using testing::SizeIs;
- using testing::UnorderedElementsAre;
- using testing::WithArg;
- using WriteBatch = ModelTypeStore::WriteBatch;
- MATCHER_P(MatchesUserEvent, expected, "") {
- if (!arg.has_user_event()) {
- *result_listener << "which is not a user event";
- return false;
- }
- const UserEventSpecifics& actual = arg.user_event();
- if (actual.event_time_usec() != expected.event_time_usec()) {
- return false;
- }
- if (actual.navigation_id() != expected.navigation_id()) {
- return false;
- }
- if (actual.session_id() != expected.session_id()) {
- return false;
- }
- return true;
- }
- UserEventSpecifics CreateSpecifics(int64_t event_time_usec,
- int64_t navigation_id,
- uint64_t session_id) {
- UserEventSpecifics specifics;
- specifics.set_event_time_usec(event_time_usec);
- specifics.set_navigation_id(navigation_id);
- specifics.set_session_id(session_id);
- return specifics;
- }
- std::unique_ptr<UserEventSpecifics> SpecificsUniquePtr(int64_t event_time_usec,
- int64_t navigation_id,
- uint64_t session_id) {
- return std::make_unique<UserEventSpecifics>(
- CreateSpecifics(event_time_usec, navigation_id, session_id));
- }
- class TestGlobalIdMapper : public GlobalIdMapper {
- public:
- void AddGlobalIdChangeObserver(GlobalIdChange callback) override {
- callback_ = std::move(callback);
- }
- int64_t GetLatestGlobalId(int64_t global_id) override {
- auto iter = id_map_.find(global_id);
- return iter == id_map_.end() ? global_id : iter->second;
- }
- void ChangeId(int64_t old_id, int64_t new_id) {
- id_map_[old_id] = new_id;
- callback_.Run(old_id, new_id);
- }
- private:
- GlobalIdChange callback_;
- std::map<int64_t, int64_t> id_map_;
- };
- class UserEventSyncBridgeTest : public testing::Test {
- protected:
- UserEventSyncBridgeTest() { ResetBridge(); }
- void ResetBridge() {
- OnceModelTypeStoreFactory store_factory;
- if (bridge_) {
- // Carry over the underlying store from previous bridge instances.
- std::unique_ptr<ModelTypeStore> store = bridge_->StealStoreForTest();
- bridge_.reset();
- store_factory =
- ModelTypeStoreTestUtil::MoveStoreToFactory(std::move(store));
- } else {
- store_factory = ModelTypeStoreTestUtil::FactoryForInMemoryStoreForTest();
- }
- bridge_ = std::make_unique<UserEventSyncBridge>(
- std::move(store_factory), mock_processor_.CreateForwardingProcessor(),
- &test_global_id_mapper_);
- }
- void WaitUntilModelReadyToSync(
- const std::string& account_id = "test_account_id") {
- base::RunLoop loop;
- base::RepeatingClosure quit_closure = loop.QuitClosure();
- // Let the bridge initialize fully, which should run ModelReadyToSync().
- ON_CALL(*processor(), ModelReadyToSync)
- .WillByDefault(InvokeWithoutArgs([=]() { quit_closure.Run(); }));
- loop.Run();
- ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(true));
- ON_CALL(*processor(), TrackedAccountId()).WillByDefault(Return(account_id));
- }
- static std::string GetStorageKey(const UserEventSpecifics& specifics) {
- return UserEventSyncBridge::GetStorageKeyFromSpecificsForTest(specifics);
- }
- UserEventSyncBridge* bridge() { return bridge_.get(); }
- MockModelTypeChangeProcessor* processor() { return &mock_processor_; }
- TestGlobalIdMapper* mapper() { return &test_global_id_mapper_; }
- std::map<std::string, sync_pb::EntitySpecifics> GetAllData() {
- base::RunLoop loop;
- std::unique_ptr<DataBatch> batch;
- bridge_->GetAllDataForDebugging(base::BindOnce(
- [](base::RunLoop* loop, std::unique_ptr<DataBatch>* out_batch,
- std::unique_ptr<DataBatch> batch) {
- *out_batch = std::move(batch);
- loop->Quit();
- },
- &loop, &batch));
- loop.Run();
- EXPECT_NE(nullptr, batch);
- std::map<std::string, sync_pb::EntitySpecifics> storage_key_to_specifics;
- if (batch != nullptr) {
- while (batch->HasNext()) {
- auto [key, data] = batch->Next();
- storage_key_to_specifics[key] = data->specifics;
- }
- }
- return storage_key_to_specifics;
- }
- std::unique_ptr<sync_pb::EntitySpecifics> GetData(
- const std::string& storage_key) {
- base::RunLoop loop;
- std::unique_ptr<DataBatch> batch;
- bridge_->GetData(
- {storage_key},
- base::BindOnce(
- [](base::RunLoop* loop, std::unique_ptr<DataBatch>* out_batch,
- std::unique_ptr<DataBatch> batch) {
- *out_batch = std::move(batch);
- loop->Quit();
- },
- &loop, &batch));
- loop.Run();
- EXPECT_NE(nullptr, batch);
- std::unique_ptr<sync_pb::EntitySpecifics> specifics;
- if (batch != nullptr && batch->HasNext()) {
- auto [key, data] = batch->Next();
- specifics = std::make_unique<sync_pb::EntitySpecifics>(data->specifics);
- EXPECT_FALSE(batch->HasNext());
- }
- return specifics;
- }
- private:
- base::test::TaskEnvironment task_environment_;
- testing::NiceMock<MockModelTypeChangeProcessor> mock_processor_;
- TestGlobalIdMapper test_global_id_mapper_;
- std::unique_ptr<UserEventSyncBridge> bridge_;
- };
- TEST_F(UserEventSyncBridgeTest, MetadataIsInitialized) {
- EXPECT_CALL(*processor(), ModelReadyToSync(NotNull()));
- WaitUntilModelReadyToSync();
- }
- TEST_F(UserEventSyncBridgeTest, SingleRecord) {
- WaitUntilModelReadyToSync();
- const UserEventSpecifics specifics(CreateSpecifics(1u, 2u, 3u));
- std::string storage_key;
- EXPECT_CALL(*processor(), Put).WillOnce(WithArg<0>(SaveArg<0>(&storage_key)));
- bridge()->RecordUserEvent(std::make_unique<UserEventSpecifics>(specifics));
- EXPECT_THAT(GetData(storage_key), Pointee(MatchesUserEvent(specifics)));
- EXPECT_THAT(GetData("bogus"), IsNull());
- EXPECT_THAT(GetAllData(),
- ElementsAre(Pair(storage_key, MatchesUserEvent(specifics))));
- }
- TEST_F(UserEventSyncBridgeTest, ApplyStopSyncChanges) {
- WaitUntilModelReadyToSync();
- const UserEventSpecifics specifics(CreateSpecifics(1u, 2u, 3u));
- bridge()->RecordUserEvent(std::make_unique<UserEventSpecifics>(specifics));
- ASSERT_THAT(GetAllData(), SizeIs(1));
- bridge()->ApplyStopSyncChanges(WriteBatch::CreateMetadataChangeList());
- // The bridge may asynchronously query the store to choose what to delete.
- base::RunLoop().RunUntilIdle();
- EXPECT_THAT(GetAllData(), IsEmpty());
- }
- TEST_F(UserEventSyncBridgeTest, MultipleRecords) {
- WaitUntilModelReadyToSync();
- std::set<std::string> unique_storage_keys;
- EXPECT_CALL(*processor(), Put)
- .Times(4)
- .WillRepeatedly(
- [&unique_storage_keys](const std::string& storage_key,
- std::unique_ptr<EntityData> entity_data,
- MetadataChangeList* metadata_change_list) {
- unique_storage_keys.insert(storage_key);
- });
- bridge()->RecordUserEvent(SpecificsUniquePtr(1u, 1u, 1u));
- bridge()->RecordUserEvent(SpecificsUniquePtr(1u, 1u, 2u));
- bridge()->RecordUserEvent(SpecificsUniquePtr(1u, 2u, 2u));
- bridge()->RecordUserEvent(SpecificsUniquePtr(2u, 2u, 2u));
- EXPECT_EQ(2u, unique_storage_keys.size());
- EXPECT_THAT(GetAllData(), SizeIs(2));
- }
- TEST_F(UserEventSyncBridgeTest, ApplySyncChanges) {
- WaitUntilModelReadyToSync();
- std::string storage_key1;
- std::string storage_key2;
- EXPECT_CALL(*processor(), Put)
- .WillOnce(WithArg<0>(SaveArg<0>(&storage_key1)))
- .WillOnce(WithArg<0>(SaveArg<0>(&storage_key2)));
- bridge()->RecordUserEvent(SpecificsUniquePtr(1u, 1u, 1u));
- bridge()->RecordUserEvent(SpecificsUniquePtr(2u, 2u, 2u));
- EXPECT_THAT(GetAllData(), SizeIs(2));
- syncer::EntityChangeList entity_change_list;
- entity_change_list.push_back(EntityChange::CreateDelete(storage_key1));
- auto error_on_delete = bridge()->ApplySyncChanges(
- bridge()->CreateMetadataChangeList(), std::move(entity_change_list));
- EXPECT_FALSE(error_on_delete);
- EXPECT_THAT(GetAllData(), SizeIs(1));
- EXPECT_THAT(GetData(storage_key1), IsNull());
- EXPECT_THAT(GetData(storage_key2), NotNull());
- }
- TEST_F(UserEventSyncBridgeTest, HandleGlobalIdChange) {
- WaitUntilModelReadyToSync();
- int64_t first_id = 11;
- int64_t second_id = 12;
- int64_t third_id = 13;
- int64_t fourth_id = 14;
- std::string storage_key;
- EXPECT_CALL(*processor(), Put).WillOnce(WithArg<0>(SaveArg<0>(&storage_key)));
- // This id update should be applied to the event as it is initially
- // recorded.
- mapper()->ChangeId(first_id, second_id);
- bridge()->RecordUserEvent(SpecificsUniquePtr(1u, first_id, 2u));
- EXPECT_THAT(GetAllData(),
- ElementsAre(Pair(storage_key, MatchesUserEvent(CreateSpecifics(
- 1u, second_id, 2u)))));
- // This id update is done while the event is "in flight", and should result in
- // it being updated and re-sent to sync.
- EXPECT_CALL(*processor(), Put(storage_key, _, _));
- mapper()->ChangeId(second_id, third_id);
- EXPECT_THAT(GetAllData(),
- ElementsAre(Pair(storage_key, MatchesUserEvent(CreateSpecifics(
- 1u, third_id, 2u)))));
- syncer::EntityChangeList entity_change_list;
- entity_change_list.push_back(EntityChange::CreateDelete(storage_key));
- auto error_on_delete = bridge()->ApplySyncChanges(
- bridge()->CreateMetadataChangeList(), std::move(entity_change_list));
- EXPECT_FALSE(error_on_delete);
- EXPECT_THAT(GetAllData(), IsEmpty());
- // This id update should be ignored, since we received commit confirmation
- // above.
- EXPECT_CALL(*processor(), Put).Times(0);
- mapper()->ChangeId(third_id, fourth_id);
- EXPECT_THAT(GetAllData(), IsEmpty());
- }
- TEST_F(UserEventSyncBridgeTest, MulipleEventsChanging) {
- WaitUntilModelReadyToSync();
- int64_t first_id = 11;
- int64_t second_id = 12;
- int64_t third_id = 13;
- int64_t fourth_id = 14;
- const UserEventSpecifics specifics1 = CreateSpecifics(101u, first_id, 2u);
- const UserEventSpecifics specifics2 = CreateSpecifics(102u, second_id, 4u);
- const UserEventSpecifics specifics3 = CreateSpecifics(103u, third_id, 6u);
- const std::string key1 = GetStorageKey(specifics1);
- const std::string key2 = GetStorageKey(specifics2);
- const std::string key3 = GetStorageKey(specifics3);
- ASSERT_NE(key1, key2);
- ASSERT_NE(key1, key3);
- ASSERT_NE(key2, key3);
- bridge()->RecordUserEvent(std::make_unique<UserEventSpecifics>(specifics1));
- bridge()->RecordUserEvent(std::make_unique<UserEventSpecifics>(specifics2));
- bridge()->RecordUserEvent(std::make_unique<UserEventSpecifics>(specifics3));
- ASSERT_THAT(GetAllData(),
- UnorderedElementsAre(Pair(key1, MatchesUserEvent(specifics1)),
- Pair(key2, MatchesUserEvent(specifics2)),
- Pair(key3, MatchesUserEvent(specifics3))));
- mapper()->ChangeId(second_id, fourth_id);
- EXPECT_THAT(
- GetAllData(),
- UnorderedElementsAre(
- Pair(key1, MatchesUserEvent(specifics1)),
- Pair(key2, MatchesUserEvent(CreateSpecifics(102u, fourth_id, 4u))),
- Pair(key3, MatchesUserEvent(specifics3))));
- mapper()->ChangeId(first_id, fourth_id);
- mapper()->ChangeId(third_id, fourth_id);
- EXPECT_THAT(
- GetAllData(),
- UnorderedElementsAre(
- Pair(key1, MatchesUserEvent(CreateSpecifics(101u, fourth_id, 2u))),
- Pair(key2, MatchesUserEvent(CreateSpecifics(102u, fourth_id, 4u))),
- Pair(key3, MatchesUserEvent(CreateSpecifics(103u, fourth_id, 6u)))));
- }
- TEST_F(UserEventSyncBridgeTest, RecordBeforeMetadataLoads) {
- ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(false));
- ON_CALL(*processor(), TrackedAccountId()).WillByDefault(Return(""));
- bridge()->RecordUserEvent(SpecificsUniquePtr(1u, 2u, 3u));
- EXPECT_CALL(*processor(), ModelReadyToSync);
- WaitUntilModelReadyToSync("account_id");
- EXPECT_THAT(GetAllData(), IsEmpty());
- }
- } // namespace
- } // namespace syncer
|