1 // Copyright 2014 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 "chrome/browser/notifications/sync_notifier/synced_notification_app_info_service.h"
6
7 #include "chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.h"
8 #include "chrome/browser/profiles/profile.h"
9 #include "chrome/test/base/testing_pref_service_syncable.h"
10 #include "chrome/test/base/testing_profile.h"
11 #include "content/public/test/test_browser_thread.h"
12 #include "sync/api/fake_sync_change_processor.h"
13 #include "sync/api/sync_change.h"
14 #include "sync/api/sync_change_processor.h"
15 #include "sync/api/sync_error_factory.h"
16 #include "sync/api/sync_error_factory_mock.h"
17 #include "sync/protocol/sync.pb.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 using sync_pb::EntitySpecifics;
21 using syncer::SyncData;
22 using syncer::SyncChange;
23 using syncer::SyncChangeList;
24 using syncer::SyncDataList;
25 using syncer::SYNCED_NOTIFICATION_APP_INFO;
26 using notifier::SyncedNotificationAppInfo;
27 using notifier::SyncedNotificationAppInfoService;
28 using sync_pb::SyncedNotificationAppInfoSpecifics;
29
30 namespace notifier {
31
32 // Extract app_info id from syncer::SyncData.
GetAppInfoId(const SyncData & sync_data)33 std::string GetAppInfoId(const SyncData& sync_data) {
34 SyncedNotificationAppInfoSpecifics specifics =
35 sync_data.GetSpecifics().synced_notification_app_info();
36
37 // TODO(petewil): It would be better if we had a designated unique identifier
38 // instead of relying on the display name to be unique.
39 return specifics.synced_notification_app_info(0).settings_display_name();
40 }
41
42 // Dummy SyncChangeProcessor used to help review what SyncChanges are pushed
43 // back up to Sync.
44 class TestChangeProcessor : public syncer::SyncChangeProcessor {
45 public:
TestChangeProcessor()46 TestChangeProcessor() {}
~TestChangeProcessor()47 virtual ~TestChangeProcessor() {}
48
49 // Store a copy of all the changes passed in so we can examine them later.
ProcessSyncChanges(const tracked_objects::Location & from_here,const SyncChangeList & change_list)50 virtual syncer::SyncError ProcessSyncChanges(
51 const tracked_objects::Location& from_here,
52 const SyncChangeList& change_list) OVERRIDE {
53 change_map_.clear();
54 for (SyncChangeList::const_iterator iter = change_list.begin();
55 iter != change_list.end();
56 ++iter) {
57 // Put the data into the change tracking map.
58 change_map_[GetAppInfoId(iter->sync_data())] = *iter;
59 }
60
61 return syncer::SyncError();
62 }
63
GetAllSyncData(syncer::ModelType type) const64 virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type)
65 const OVERRIDE {
66 return syncer::SyncDataList();
67 }
68
change_list_size()69 size_t change_list_size() { return change_map_.size(); }
70
ContainsId(const std::string & id)71 bool ContainsId(const std::string& id) {
72 return change_map_.find(id) != change_map_.end();
73 }
74
GetChangeById(const std::string & id)75 SyncChange GetChangeById(const std::string& id) {
76 EXPECT_TRUE(ContainsId(id));
77 return change_map_[id];
78 }
79
80 private:
81 // Track the changes received in ProcessSyncChanges.
82 std::map<std::string, SyncChange> change_map_;
83
84 DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor);
85 };
86
87 class SyncedNotificationAppInfoServiceTest : public testing::Test {
88
89 public:
SyncedNotificationAppInfoServiceTest()90 SyncedNotificationAppInfoServiceTest()
91 : sync_processor_(new TestChangeProcessor),
92 sync_processor_delegate_(
93 new syncer::FakeSyncChangeProcessor()) {}
94
~SyncedNotificationAppInfoServiceTest()95 virtual ~SyncedNotificationAppInfoServiceTest() {}
96
97 // Overrides from testing::Test.
SetUp()98 virtual void SetUp() { profile_.reset(new TestingProfile()); }
99
100 // Introduce some sample test data into the system.
AddTestingAppInfosToList(SyncedNotificationAppInfoService * app_info_service)101 void AddTestingAppInfosToList(
102 SyncedNotificationAppInfoService* app_info_service) {
103 // Create the app_info struct, setting the settings display name.
104
105 // The sending_service_infos_ list will take ownership of this pointer.
106 SyncedNotificationAppInfo* test_item1 = new SyncedNotificationAppInfo(
107 NULL, kSendingService1Name, app_info_service);
108
109 // Add some App IDs.
110 test_item1->AddAppId(kAppId1);
111 test_item1->AddAppId(kAppId2);
112
113 // Set this icon GURL.
114 test_item1->SetSettingsURLs(GURL(kTestIconUrl), GURL());
115
116 // Add to the list.
117 app_info_service->sending_service_infos_.push_back(test_item1);
118
119 // Add a second test item for another service.
120 SyncedNotificationAppInfo* test_item2 = new SyncedNotificationAppInfo(
121 NULL, kSendingService2Name, app_info_service);
122
123 // Add some App IDs.
124 test_item2->AddAppId(kAppId4);
125 test_item2->AddAppId(kAppId5);
126
127 // Set thi icon GURL.
128 test_item2->SetSettingsURLs(GURL(kTestIconUrl), GURL());
129
130 // Add to the list.
131 app_info_service->sending_service_infos_.push_back(test_item2);
132 }
133
134 // Put some representative test data into the AppInfo protobuf.
FillProtobufWithTestData1(sync_pb::SyncedNotificationAppInfo & protobuf)135 static void FillProtobufWithTestData1(
136 sync_pb::SyncedNotificationAppInfo& protobuf) {
137 protobuf.add_app_id(std::string(kAppId1));
138 protobuf.add_app_id(std::string(kAppId2));
139 protobuf.set_settings_display_name(kSendingService1Name);
140 protobuf.set_info_url(kTestInfoUrl);
141 protobuf.mutable_icon()->set_url(kTestIconUrl);
142 }
143
FillProtobufWithTestData2(sync_pb::SyncedNotificationAppInfo & protobuf)144 static void FillProtobufWithTestData2(
145 sync_pb::SyncedNotificationAppInfo& protobuf) {
146 protobuf.add_app_id(std::string(kAppId3));
147 protobuf.set_settings_display_name(kSendingService1Name);
148 protobuf.mutable_icon()->set_url(kTestIconUrl);
149 }
150
151 // Helper to create syncer::SyncChange.
CreateSyncChange(SyncChange::SyncChangeType type,const std::string & settings_display_name,const std::string & info_url,const std::string & icon_url,const std::string & app_id1,const std::string & app_id2)152 static SyncChange CreateSyncChange(SyncChange::SyncChangeType type,
153 const std::string& settings_display_name,
154 const std::string& info_url,
155 const std::string& icon_url,
156 const std::string& app_id1,
157 const std::string& app_id2) {
158
159 return SyncChange(
160 FROM_HERE,
161 type,
162 CreateSyncData(
163 settings_display_name, info_url, icon_url, app_id1, app_id2));
164 }
165
166 // Build a SyncData object to look like what Sync would deliver.
CreateSyncData(const std::string & settings_display_name,const std::string & info_url,const std::string & icon_url,const std::string & app_id1,const std::string & app_id2)167 static SyncData CreateSyncData(const std::string& settings_display_name,
168 const std::string& info_url,
169 const std::string& icon_url,
170 const std::string& app_id1,
171 const std::string& app_id2) {
172 // CreateLocalData makes a copy of this, so it can safely live on the stack.
173 EntitySpecifics entity_specifics;
174 EXPECT_FALSE(app_id1.empty());
175
176 sync_pb::SyncedNotificationAppInfoSpecifics* specifics =
177 entity_specifics.mutable_synced_notification_app_info();
178
179 // Add a synced_notification_app_info object.
180 specifics->add_synced_notification_app_info();
181 sync_pb::SyncedNotificationAppInfo* app_info =
182 specifics->mutable_synced_notification_app_info(0);
183
184 // Add the key, the settings display name.
185 app_info->set_settings_display_name(settings_display_name);
186
187 // Add the welcome notification info URL.
188 app_info->set_info_url(info_url);
189
190 // Add the icon URL.
191 app_info->mutable_icon()->set_url(icon_url);
192
193 // Add the app IDs.
194 app_info->add_app_id(app_id1);
195
196 // Only add the second if it is non-empty
197 if (!app_id2.empty()) {
198 app_info->add_app_id(app_id2);
199 }
200
201 // Create the sync data.
202 SyncData sync_data =
203 SyncData::CreateLocalData("syncer::SYNCED_NOTIFICATION_APP_INFO",
204 "SyncedNotificationAppInfoServiceUnitTest",
205 entity_specifics);
206
207 return sync_data;
208 }
209
processor()210 TestChangeProcessor* processor() {
211 return static_cast<TestChangeProcessor*>(sync_processor_.get());
212 }
213
PassProcessor()214 scoped_ptr<syncer::SyncChangeProcessor> PassProcessor() {
215 return sync_processor_delegate_.Pass();
216 }
217
218 protected:
219 scoped_ptr<TestingProfile> profile_;
220
221 private:
222 scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
223 scoped_ptr<syncer::SyncChangeProcessor> sync_processor_delegate_;
224
225 DISALLOW_COPY_AND_ASSIGN(SyncedNotificationAppInfoServiceTest);
226 };
227
228 // Null data case - we have no data, and sync has no data when we start up.
TEST_F(SyncedNotificationAppInfoServiceTest,MergeDataAndStartSyncingTest)229 TEST_F(SyncedNotificationAppInfoServiceTest, MergeDataAndStartSyncingTest) {
230 SyncedNotificationAppInfoService app_info_service(profile_.get());
231
232 app_info_service.MergeDataAndStartSyncing(
233 SYNCED_NOTIFICATION_APP_INFO,
234 SyncDataList(), // Empty.
235 PassProcessor(),
236 scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock()));
237
238 EXPECT_EQ(static_cast<size_t>(0),
239 app_info_service.sending_service_infos_size());
240 }
241
242 // Process sync changes when there is no local data.
TEST_F(SyncedNotificationAppInfoServiceTest,ProcessSyncChangesEmptyModel)243 TEST_F(SyncedNotificationAppInfoServiceTest, ProcessSyncChangesEmptyModel) {
244 // We initially have no data.
245 SyncedNotificationAppInfoService app_info_service(profile_.get());
246 app_info_service.set_avoid_bitmap_fetching_for_test(true);
247
248 // Set up an ADD.
249 SyncChangeList changes;
250 changes.push_back(CreateSyncChange(SyncChange::ACTION_ADD,
251 kSendingService1Name,
252 kTestInfoUrl,
253 kTestIconUrl,
254 kAppId1,
255 kAppId2));
256
257 // Process the changes we built.
258 app_info_service.ProcessSyncChanges(FROM_HERE, changes);
259
260 // Verify sync change made it to the SyncedNotificationAppInfo list.
261 SyncedNotificationAppInfo* app_info1 =
262 app_info_service.FindSyncedNotificationAppInfoByName(
263 kSendingService1Name);
264 EXPECT_NE(static_cast<SyncedNotificationAppInfo*>(NULL), app_info1);
265 EXPECT_TRUE(app_info1->HasAppId(kAppId1));
266 EXPECT_TRUE(app_info1->HasAppId(kAppId2));
267 EXPECT_FALSE(app_info1->HasAppId(kAppId3));
268 EXPECT_EQ(app_info1->settings_icon_url(), GURL(kTestIconUrl));
269 }
270
271 // Process sync changes when there is local data.
TEST_F(SyncedNotificationAppInfoServiceTest,ProcessSyncChangesNonEmptyModel)272 TEST_F(SyncedNotificationAppInfoServiceTest, ProcessSyncChangesNonEmptyModel) {
273 SyncedNotificationAppInfoService app_info_service(profile_.get());
274 app_info_service.set_avoid_bitmap_fetching_for_test(true);
275
276 // Create some local fake data. We rely on the specific ids set up here.
277 AddTestingAppInfosToList(&app_info_service);
278
279 // Set up an UPDATE.
280 SyncChangeList changes;
281
282 changes.push_back(CreateSyncChange(SyncChange::ACTION_UPDATE,
283 kSendingService1Name,
284 kTestInfoUrl,
285 kTestIconUrl,
286 kAppId1,
287 kAppId3));
288
289 // Simulate incoming changed sync data at runtime.
290 app_info_service.ProcessSyncChanges(FROM_HERE, changes);
291
292 // We should find that the first item now has a different set of app ids.
293 SyncedNotificationAppInfo* app_info1 =
294 app_info_service.FindSyncedNotificationAppInfoByName(
295 kSendingService1Name);
296 EXPECT_NE(static_cast<SyncedNotificationAppInfo*>(NULL), app_info1);
297 EXPECT_TRUE(app_info1->HasAppId(kAppId1));
298 EXPECT_FALSE(app_info1->HasAppId(kAppId2));
299 EXPECT_TRUE(app_info1->HasAppId(kAppId3));
300 EXPECT_EQ(app_info1->settings_icon_url(), GURL(kTestIconUrl));
301 }
302
303 // Test ProcessIncomingAppInfoProtobuf with an add.
TEST_F(SyncedNotificationAppInfoServiceTest,ProcessIncomingAppInfoProtobufAddTest)304 TEST_F(SyncedNotificationAppInfoServiceTest,
305 ProcessIncomingAppInfoProtobufAddTest) {
306 // Get an app info service object.
307 SyncedNotificationAppInfoService app_info_service(profile_.get());
308 app_info_service.set_avoid_bitmap_fetching_for_test(true);
309
310 // Get an app info protobuf.
311 sync_pb::SyncedNotificationAppInfo protobuf;
312 FillProtobufWithTestData1(protobuf);
313
314 // Call the function we are testing.
315 app_info_service.ProcessIncomingAppInfoProtobuf(protobuf);
316
317 // Ensure that we now have an app_info in our list, and it looks like we
318 // expect.
319 notifier::SyncedNotificationAppInfo* found_app_info;
320 found_app_info = app_info_service.FindSyncedNotificationAppInfoByName(
321 kSendingService1Name);
322 EXPECT_NE(static_cast<notifier::SyncedNotificationAppInfo*>(NULL),
323 found_app_info);
324 EXPECT_TRUE(found_app_info->HasAppId(kAppId1));
325 EXPECT_TRUE(found_app_info->HasAppId(kAppId2));
326 }
327
328 // Test ProcessIncomingAppInfoProtobuf with an update
TEST_F(SyncedNotificationAppInfoServiceTest,ProcessIncomingAppInfoProtobufUpdateTest)329 TEST_F(SyncedNotificationAppInfoServiceTest,
330 ProcessIncomingAppInfoProtobufUpdateTest) {
331 // Get an app info service object.
332 SyncedNotificationAppInfoService app_info_service(profile_.get());
333 app_info_service.set_avoid_bitmap_fetching_for_test(true);
334
335 // Make an app info with the same display name as the first one in the test
336 // data.
337 sync_pb::SyncedNotificationAppInfo protobuf1;
338 FillProtobufWithTestData1(protobuf1);
339 app_info_service.ProcessIncomingAppInfoProtobuf(protobuf1);
340
341 // Ensure that we now have an app_info in our list, and it looks like we
342 // expect.
343 notifier::SyncedNotificationAppInfo* found_app_info1;
344 found_app_info1 = app_info_service.FindSyncedNotificationAppInfoByName(
345 kSendingService1Name);
346 EXPECT_NE(static_cast<notifier::SyncedNotificationAppInfo*>(NULL),
347 found_app_info1);
348 EXPECT_TRUE(found_app_info1->HasAppId(kAppId1));
349 EXPECT_TRUE(found_app_info1->HasAppId(kAppId2));
350
351 // Make an update to the protobuf that has already been sent.
352 app_info_service.FreeSyncedNotificationAppInfoByName(kSendingService1Name);
353 // Change appid1 to appid3
354 sync_pb::SyncedNotificationAppInfo protobuf2;
355 FillProtobufWithTestData2(protobuf2);
356 app_info_service.ProcessIncomingAppInfoProtobuf(protobuf2);
357
358 // Ensure we have the same named app info as before, but it has the new
359 // contents.
360 notifier::SyncedNotificationAppInfo* found_app_info2;
361 found_app_info2 = app_info_service.FindSyncedNotificationAppInfoByName(
362 kSendingService1Name);
363 EXPECT_NE(static_cast<notifier::SyncedNotificationAppInfo*>(NULL),
364 found_app_info2);
365 EXPECT_FALSE(found_app_info2->HasAppId(kAppId1));
366 EXPECT_TRUE(found_app_info2->HasAppId(kAppId3));
367 }
368
369 // Test our function that creates a synced notification from a protobuf.
TEST_F(SyncedNotificationAppInfoServiceTest,CreateSyncedNotificationAppInfoFromProtobufTest)370 TEST_F(SyncedNotificationAppInfoServiceTest,
371 CreateSyncedNotificationAppInfoFromProtobufTest) {
372 SyncedNotificationAppInfoService app_info_service(profile_.get());
373 // Build a protobuf and fill it with data.
374 sync_pb::SyncedNotificationAppInfo protobuf;
375 FillProtobufWithTestData1(protobuf);
376
377 scoped_ptr<SyncedNotificationAppInfo> app_info;
378 app_info =
379 app_info_service.CreateSyncedNotificationAppInfoFromProtobuf(protobuf);
380
381 // Ensure the app info class has the fields we expect.
382 EXPECT_EQ(std::string(kSendingService1Name),
383 app_info->settings_display_name());
384 EXPECT_EQ(GURL(kTestInfoUrl), app_info->welcome_link_url());
385 EXPECT_TRUE(app_info->HasAppId(kAppId1));
386 EXPECT_TRUE(app_info->HasAppId(kAppId2));
387 EXPECT_EQ(GURL(std::string(kTestIconUrl)), app_info->settings_icon_url());
388 }
389
390 // Test our find by sending service name function.
TEST_F(SyncedNotificationAppInfoServiceTest,FindSyncedNotificationAppInfoByNameTest)391 TEST_F(SyncedNotificationAppInfoServiceTest,
392 FindSyncedNotificationAppInfoByNameTest) {
393 SyncedNotificationAppInfoService app_info_service(profile_.get());
394
395 AddTestingAppInfosToList(&app_info_service);
396
397 SyncedNotificationAppInfo* found;
398
399 found = app_info_service.FindSyncedNotificationAppInfoByName(
400 kSendingService1Name);
401
402 EXPECT_NE(static_cast<SyncedNotificationAppInfo*>(NULL), found);
403 EXPECT_EQ(std::string(kSendingService1Name), found->settings_display_name());
404
405 found = app_info_service.FindSyncedNotificationAppInfoByName(
406 kSendingService3Name);
407 EXPECT_EQ(NULL, found);
408 }
409
410 // Test our find by AppId function.
TEST_F(SyncedNotificationAppInfoServiceTest,FindSyncedNotificationAppInfoByAppIdTest)411 TEST_F(SyncedNotificationAppInfoServiceTest,
412 FindSyncedNotificationAppInfoByAppIdTest) {
413 SyncedNotificationAppInfoService app_info_service(profile_.get());
414
415 AddTestingAppInfosToList(&app_info_service);
416
417 SyncedNotificationAppInfo* found;
418
419 found = app_info_service.FindSyncedNotificationAppInfoByAppId(kAppId1);
420
421 EXPECT_NE(static_cast<SyncedNotificationAppInfo*>(NULL), found);
422 EXPECT_EQ(std::string(kSendingService1Name), found->settings_display_name());
423
424 found = app_info_service.FindSyncedNotificationAppInfoByName(kAppId5);
425 EXPECT_EQ(NULL, found);
426 }
427
428 // Test our find sending service name by app id function.
TEST_F(SyncedNotificationAppInfoServiceTest,FindSendingServiceNameFromAppIdTest)429 TEST_F(SyncedNotificationAppInfoServiceTest,
430 FindSendingServiceNameFromAppIdTest) {
431 SyncedNotificationAppInfoService app_info_service(profile_.get());
432
433 AddTestingAppInfosToList(&app_info_service);
434
435 std::string found_name;
436
437 found_name = app_info_service.FindSendingServiceNameFromAppId(kAppId1);
438
439 EXPECT_EQ(std::string(kSendingService1Name), found_name);
440
441 found_name = app_info_service.FindSendingServiceNameFromAppId(kAppId6);
442 EXPECT_TRUE(found_name.empty());
443 }
444
445 // Test our delete function.
TEST_F(SyncedNotificationAppInfoServiceTest,FreeSyncedNotificationAppInfoByNameTest)446 TEST_F(SyncedNotificationAppInfoServiceTest,
447 FreeSyncedNotificationAppInfoByNameTest) {
448 SyncedNotificationAppInfoService app_info_service(profile_.get());
449
450 AddTestingAppInfosToList(&app_info_service);
451
452 SyncedNotificationAppInfo* found;
453
454 app_info_service.FreeSyncedNotificationAppInfoByName(kSendingService1Name);
455 found = app_info_service.FindSyncedNotificationAppInfoByName(
456 kSendingService1Name);
457 EXPECT_EQ(NULL, found);
458 }
459
TEST_F(SyncedNotificationAppInfoServiceTest,GetAllSendingServiceSettingsDataTest)460 TEST_F(SyncedNotificationAppInfoServiceTest,
461 GetAllSendingServiceSettingsDataTest) {
462 SyncedNotificationAppInfoService app_info_service(profile_.get());
463
464 AddTestingAppInfosToList(&app_info_service);
465
466 std::vector<SyncedNotificationSendingServiceSettingsData> data;
467 data = app_info_service.GetAllSendingServiceSettingsData();
468
469 EXPECT_EQ(static_cast<unsigned int>(2), data.size());
470 EXPECT_EQ(kSendingService1Name, data[0].settings_display_name);
471 EXPECT_EQ(kSendingService2Name, data[1].settings_display_name);
472 }
473
474 } // namespace notifier
475