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. 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: 46 TestChangeProcessor() {} 47 virtual ~TestChangeProcessor() {} 48 49 // Store a copy of all the changes passed in so we can examine them later. 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 64 virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) 65 const OVERRIDE { 66 return syncer::SyncDataList(); 67 } 68 69 size_t change_list_size() { return change_map_.size(); } 70 71 bool ContainsId(const std::string& id) { 72 return change_map_.find(id) != change_map_.end(); 73 } 74 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: 90 SyncedNotificationAppInfoServiceTest() 91 : sync_processor_(new TestChangeProcessor), 92 sync_processor_delegate_( 93 new syncer::FakeSyncChangeProcessor()) {} 94 95 virtual ~SyncedNotificationAppInfoServiceTest() {} 96 97 // Overrides from testing::Test. 98 virtual void SetUp() { profile_.reset(new TestingProfile()); } 99 100 // Introduce some sample test data into the system. 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. 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 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. 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. 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 210 TestChangeProcessor* processor() { 211 return static_cast<TestChangeProcessor*>(sync_processor_.get()); 212 } 213 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. 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. 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. 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. 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 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. 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. 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. 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. 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. 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 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