1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/sync_user_events/user_event_sync_bridge.h"
6
7 #include <map>
8 #include <set>
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/run_loop.h"
14 #include "base/test/task_environment.h"
15 #include "components/sync/model/data_batch.h"
16 #include "components/sync/protocol/sync.pb.h"
17 #include "components/sync/test/model/mock_model_type_change_processor.h"
18 #include "components/sync/test/model/model_type_store_test_util.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 namespace syncer {
23 namespace {
24
25 using sync_pb::UserEventSpecifics;
26 using testing::_;
27 using testing::ElementsAre;
28 using testing::Eq;
29 using testing::InvokeWithoutArgs;
30 using testing::IsEmpty;
31 using testing::IsNull;
32 using testing::NotNull;
33 using testing::Pair;
34 using testing::Pointee;
35 using testing::Return;
36 using testing::SaveArg;
37 using testing::SizeIs;
38 using testing::UnorderedElementsAre;
39 using testing::WithArg;
40 using WriteBatch = ModelTypeStore::WriteBatch;
41
42 MATCHER_P(MatchesUserEvent, expected, "") {
43 if (!arg.has_user_event()) {
44 *result_listener << "which is not a user event";
45 return false;
46 }
47 const UserEventSpecifics& actual = arg.user_event();
48 if (actual.event_time_usec() != expected.event_time_usec()) {
49 return false;
50 }
51 if (actual.navigation_id() != expected.navigation_id()) {
52 return false;
53 }
54 if (actual.session_id() != expected.session_id()) {
55 return false;
56 }
57 return true;
58 }
59
CreateSpecifics(int64_t event_time_usec,int64_t navigation_id,uint64_t session_id)60 UserEventSpecifics CreateSpecifics(int64_t event_time_usec,
61 int64_t navigation_id,
62 uint64_t session_id) {
63 UserEventSpecifics specifics;
64 specifics.set_event_time_usec(event_time_usec);
65 specifics.set_navigation_id(navigation_id);
66 specifics.set_session_id(session_id);
67 return specifics;
68 }
69
SpecificsUniquePtr(int64_t event_time_usec,int64_t navigation_id,uint64_t session_id)70 std::unique_ptr<UserEventSpecifics> SpecificsUniquePtr(int64_t event_time_usec,
71 int64_t navigation_id,
72 uint64_t session_id) {
73 return std::make_unique<UserEventSpecifics>(
74 CreateSpecifics(event_time_usec, navigation_id, session_id));
75 }
76
77 class TestGlobalIdMapper : public GlobalIdMapper {
78 public:
AddGlobalIdChangeObserver(GlobalIdChange callback)79 void AddGlobalIdChangeObserver(GlobalIdChange callback) override {
80 callback_ = std::move(callback);
81 }
82
GetLatestGlobalId(int64_t global_id)83 int64_t GetLatestGlobalId(int64_t global_id) override {
84 auto iter = id_map_.find(global_id);
85 return iter == id_map_.end() ? global_id : iter->second;
86 }
87
ChangeId(int64_t old_id,int64_t new_id)88 void ChangeId(int64_t old_id, int64_t new_id) {
89 id_map_[old_id] = new_id;
90 callback_.Run(old_id, new_id);
91 }
92
93 private:
94 GlobalIdChange callback_;
95 std::map<int64_t, int64_t> id_map_;
96 };
97
98 class UserEventSyncBridgeTest : public testing::Test {
99 protected:
UserEventSyncBridgeTest()100 UserEventSyncBridgeTest() { ResetBridge(); }
101
ResetBridge()102 void ResetBridge() {
103 OnceModelTypeStoreFactory store_factory;
104 if (bridge_) {
105 // Carry over the underlying store from previous bridge instances.
106 std::unique_ptr<ModelTypeStore> store = bridge_->StealStoreForTest();
107 bridge_.reset();
108 store_factory =
109 ModelTypeStoreTestUtil::MoveStoreToFactory(std::move(store));
110 } else {
111 store_factory = ModelTypeStoreTestUtil::FactoryForInMemoryStoreForTest();
112 }
113 bridge_ = std::make_unique<UserEventSyncBridge>(
114 std::move(store_factory), mock_processor_.CreateForwardingProcessor(),
115 &test_global_id_mapper_);
116 }
117
WaitUntilModelReadyToSync(const std::string & account_id="test_account_id")118 void WaitUntilModelReadyToSync(
119 const std::string& account_id = "test_account_id") {
120 base::RunLoop loop;
121 base::RepeatingClosure quit_closure = loop.QuitClosure();
122 // Let the bridge initialize fully, which should run ModelReadyToSync().
123 ON_CALL(*processor(), ModelReadyToSync(_))
124 .WillByDefault(InvokeWithoutArgs([=]() { quit_closure.Run(); }));
125 loop.Run();
126 ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(true));
127 ON_CALL(*processor(), TrackedAccountId()).WillByDefault(Return(account_id));
128 }
129
GetStorageKey(const UserEventSpecifics & specifics)130 static std::string GetStorageKey(const UserEventSpecifics& specifics) {
131 return UserEventSyncBridge::GetStorageKeyFromSpecificsForTest(specifics);
132 }
133
bridge()134 UserEventSyncBridge* bridge() { return bridge_.get(); }
processor()135 MockModelTypeChangeProcessor* processor() { return &mock_processor_; }
mapper()136 TestGlobalIdMapper* mapper() { return &test_global_id_mapper_; }
137
GetAllData()138 std::map<std::string, sync_pb::EntitySpecifics> GetAllData() {
139 base::RunLoop loop;
140 std::unique_ptr<DataBatch> batch;
141 bridge_->GetAllDataForDebugging(base::BindOnce(
142 [](base::RunLoop* loop, std::unique_ptr<DataBatch>* out_batch,
143 std::unique_ptr<DataBatch> batch) {
144 *out_batch = std::move(batch);
145 loop->Quit();
146 },
147 &loop, &batch));
148 loop.Run();
149 EXPECT_NE(nullptr, batch);
150
151 std::map<std::string, sync_pb::EntitySpecifics> storage_key_to_specifics;
152 if (batch != nullptr) {
153 while (batch->HasNext()) {
154 const syncer::KeyAndData& pair = batch->Next();
155 storage_key_to_specifics[pair.first] = pair.second->specifics;
156 }
157 }
158 return storage_key_to_specifics;
159 }
160
GetData(const std::string & storage_key)161 std::unique_ptr<sync_pb::EntitySpecifics> GetData(
162 const std::string& storage_key) {
163 base::RunLoop loop;
164 std::unique_ptr<DataBatch> batch;
165 bridge_->GetData(
166 {storage_key},
167 base::BindOnce(
168 [](base::RunLoop* loop, std::unique_ptr<DataBatch>* out_batch,
169 std::unique_ptr<DataBatch> batch) {
170 *out_batch = std::move(batch);
171 loop->Quit();
172 },
173 &loop, &batch));
174 loop.Run();
175 EXPECT_NE(nullptr, batch);
176
177 std::unique_ptr<sync_pb::EntitySpecifics> specifics;
178 if (batch != nullptr && batch->HasNext()) {
179 const syncer::KeyAndData& pair = batch->Next();
180 specifics =
181 std::make_unique<sync_pb::EntitySpecifics>(pair.second->specifics);
182 EXPECT_FALSE(batch->HasNext());
183 }
184 return specifics;
185 }
186
187 private:
188 base::test::TaskEnvironment task_environment_;
189 testing::NiceMock<MockModelTypeChangeProcessor> mock_processor_;
190 TestGlobalIdMapper test_global_id_mapper_;
191 std::unique_ptr<UserEventSyncBridge> bridge_;
192 };
193
TEST_F(UserEventSyncBridgeTest,MetadataIsInitialized)194 TEST_F(UserEventSyncBridgeTest, MetadataIsInitialized) {
195 EXPECT_CALL(*processor(), ModelReadyToSync(NotNull()));
196 WaitUntilModelReadyToSync();
197 }
198
TEST_F(UserEventSyncBridgeTest,SingleRecord)199 TEST_F(UserEventSyncBridgeTest, SingleRecord) {
200 WaitUntilModelReadyToSync();
201 const UserEventSpecifics specifics(CreateSpecifics(1u, 2u, 3u));
202 std::string storage_key;
203 EXPECT_CALL(*processor(), Put(_, _, _))
204 .WillOnce(WithArg<0>(SaveArg<0>(&storage_key)));
205 bridge()->RecordUserEvent(std::make_unique<UserEventSpecifics>(specifics));
206
207 EXPECT_THAT(GetData(storage_key), Pointee(MatchesUserEvent(specifics)));
208 EXPECT_THAT(GetData("bogus"), IsNull());
209 EXPECT_THAT(GetAllData(),
210 ElementsAre(Pair(storage_key, MatchesUserEvent(specifics))));
211 }
212
TEST_F(UserEventSyncBridgeTest,ApplyStopSyncChanges)213 TEST_F(UserEventSyncBridgeTest, ApplyStopSyncChanges) {
214 WaitUntilModelReadyToSync();
215 const UserEventSpecifics specifics(CreateSpecifics(1u, 2u, 3u));
216 bridge()->RecordUserEvent(std::make_unique<UserEventSpecifics>(specifics));
217 ASSERT_THAT(GetAllData(), SizeIs(1));
218
219 bridge()->ApplyStopSyncChanges(WriteBatch::CreateMetadataChangeList());
220 // The bridge may asynchronously query the store to choose what to delete.
221 base::RunLoop().RunUntilIdle();
222
223 EXPECT_THAT(GetAllData(), IsEmpty());
224 }
225
TEST_F(UserEventSyncBridgeTest,MultipleRecords)226 TEST_F(UserEventSyncBridgeTest, MultipleRecords) {
227 WaitUntilModelReadyToSync();
228 std::set<std::string> unique_storage_keys;
229 EXPECT_CALL(*processor(), Put(_, _, _))
230 .Times(4)
231 .WillRepeatedly(
232 [&unique_storage_keys](const std::string& storage_key,
233 std::unique_ptr<EntityData> entity_data,
234 MetadataChangeList* metadata_change_list) {
235 unique_storage_keys.insert(storage_key);
236 });
237
238 bridge()->RecordUserEvent(SpecificsUniquePtr(1u, 1u, 1u));
239 bridge()->RecordUserEvent(SpecificsUniquePtr(1u, 1u, 2u));
240 bridge()->RecordUserEvent(SpecificsUniquePtr(1u, 2u, 2u));
241 bridge()->RecordUserEvent(SpecificsUniquePtr(2u, 2u, 2u));
242
243 EXPECT_EQ(2u, unique_storage_keys.size());
244 EXPECT_THAT(GetAllData(), SizeIs(2));
245 }
246
TEST_F(UserEventSyncBridgeTest,ApplySyncChanges)247 TEST_F(UserEventSyncBridgeTest, ApplySyncChanges) {
248 WaitUntilModelReadyToSync();
249 std::string storage_key1;
250 std::string storage_key2;
251 EXPECT_CALL(*processor(), Put(_, _, _))
252 .WillOnce(WithArg<0>(SaveArg<0>(&storage_key1)))
253 .WillOnce(WithArg<0>(SaveArg<0>(&storage_key2)));
254
255 bridge()->RecordUserEvent(SpecificsUniquePtr(1u, 1u, 1u));
256 bridge()->RecordUserEvent(SpecificsUniquePtr(2u, 2u, 2u));
257 EXPECT_THAT(GetAllData(), SizeIs(2));
258
259 syncer::EntityChangeList entity_change_list;
260 entity_change_list.push_back(EntityChange::CreateDelete(storage_key1));
261 auto error_on_delete = bridge()->ApplySyncChanges(
262 bridge()->CreateMetadataChangeList(), std::move(entity_change_list));
263 EXPECT_FALSE(error_on_delete);
264 EXPECT_THAT(GetAllData(), SizeIs(1));
265 EXPECT_THAT(GetData(storage_key1), IsNull());
266 EXPECT_THAT(GetData(storage_key2), NotNull());
267 }
268
TEST_F(UserEventSyncBridgeTest,HandleGlobalIdChange)269 TEST_F(UserEventSyncBridgeTest, HandleGlobalIdChange) {
270 WaitUntilModelReadyToSync();
271
272 int64_t first_id = 11;
273 int64_t second_id = 12;
274 int64_t third_id = 13;
275 int64_t fourth_id = 14;
276
277 std::string storage_key;
278 EXPECT_CALL(*processor(), Put(_, _, _))
279 .WillOnce(WithArg<0>(SaveArg<0>(&storage_key)));
280
281 // This id update should be applied to the event as it is initially
282 // recorded.
283 mapper()->ChangeId(first_id, second_id);
284 bridge()->RecordUserEvent(SpecificsUniquePtr(1u, first_id, 2u));
285 EXPECT_THAT(GetAllData(),
286 ElementsAre(Pair(storage_key, MatchesUserEvent(CreateSpecifics(
287 1u, second_id, 2u)))));
288
289 // This id update is done while the event is "in flight", and should result in
290 // it being updated and re-sent to sync.
291 EXPECT_CALL(*processor(), Put(storage_key, _, _));
292 mapper()->ChangeId(second_id, third_id);
293 EXPECT_THAT(GetAllData(),
294 ElementsAre(Pair(storage_key, MatchesUserEvent(CreateSpecifics(
295 1u, third_id, 2u)))));
296 syncer::EntityChangeList entity_change_list;
297 entity_change_list.push_back(EntityChange::CreateDelete(storage_key));
298 auto error_on_delete = bridge()->ApplySyncChanges(
299 bridge()->CreateMetadataChangeList(), std::move(entity_change_list));
300 EXPECT_FALSE(error_on_delete);
301 EXPECT_THAT(GetAllData(), IsEmpty());
302
303 // This id update should be ignored, since we received commit confirmation
304 // above.
305 EXPECT_CALL(*processor(), Put(_, _, _)).Times(0);
306 mapper()->ChangeId(third_id, fourth_id);
307 EXPECT_THAT(GetAllData(), IsEmpty());
308 }
309
TEST_F(UserEventSyncBridgeTest,MulipleEventsChanging)310 TEST_F(UserEventSyncBridgeTest, MulipleEventsChanging) {
311 WaitUntilModelReadyToSync();
312
313 int64_t first_id = 11;
314 int64_t second_id = 12;
315 int64_t third_id = 13;
316 int64_t fourth_id = 14;
317 const UserEventSpecifics specifics1 = CreateSpecifics(101u, first_id, 2u);
318 const UserEventSpecifics specifics2 = CreateSpecifics(102u, second_id, 4u);
319 const UserEventSpecifics specifics3 = CreateSpecifics(103u, third_id, 6u);
320 const std::string key1 = GetStorageKey(specifics1);
321 const std::string key2 = GetStorageKey(specifics2);
322 const std::string key3 = GetStorageKey(specifics3);
323 ASSERT_NE(key1, key2);
324 ASSERT_NE(key1, key3);
325 ASSERT_NE(key2, key3);
326
327 bridge()->RecordUserEvent(std::make_unique<UserEventSpecifics>(specifics1));
328 bridge()->RecordUserEvent(std::make_unique<UserEventSpecifics>(specifics2));
329 bridge()->RecordUserEvent(std::make_unique<UserEventSpecifics>(specifics3));
330 ASSERT_THAT(GetAllData(),
331 UnorderedElementsAre(Pair(key1, MatchesUserEvent(specifics1)),
332 Pair(key2, MatchesUserEvent(specifics2)),
333 Pair(key3, MatchesUserEvent(specifics3))));
334
335 mapper()->ChangeId(second_id, fourth_id);
336 EXPECT_THAT(
337 GetAllData(),
338 UnorderedElementsAre(
339 Pair(key1, MatchesUserEvent(specifics1)),
340 Pair(key2, MatchesUserEvent(CreateSpecifics(102u, fourth_id, 4u))),
341 Pair(key3, MatchesUserEvent(specifics3))));
342
343 mapper()->ChangeId(first_id, fourth_id);
344 mapper()->ChangeId(third_id, fourth_id);
345 EXPECT_THAT(
346 GetAllData(),
347 UnorderedElementsAre(
348 Pair(key1, MatchesUserEvent(CreateSpecifics(101u, fourth_id, 2u))),
349 Pair(key2, MatchesUserEvent(CreateSpecifics(102u, fourth_id, 4u))),
350 Pair(key3, MatchesUserEvent(CreateSpecifics(103u, fourth_id, 6u)))));
351 }
352
TEST_F(UserEventSyncBridgeTest,RecordBeforeMetadataLoads)353 TEST_F(UserEventSyncBridgeTest, RecordBeforeMetadataLoads) {
354 ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(false));
355 ON_CALL(*processor(), TrackedAccountId()).WillByDefault(Return(""));
356 bridge()->RecordUserEvent(SpecificsUniquePtr(1u, 2u, 3u));
357 EXPECT_CALL(*processor(), ModelReadyToSync(_));
358 WaitUntilModelReadyToSync("account_id");
359 EXPECT_THAT(GetAllData(), IsEmpty());
360 }
361
362 } // namespace
363
364 } // namespace syncer
365