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/extensions/api/synced_notifications_private/synced_notifications_shim.h" 6 7 #include "base/json/json_writer.h" 8 #include "extensions/browser/event_router.h" 9 #include "sync/api/fake_sync_change_processor.h" 10 #include "sync/api/sync_change.h" 11 #include "sync/api/sync_error_factory.h" 12 #include "sync/protocol/sync.pb.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 15 using namespace extensions; 16 using namespace extensions::api; 17 18 namespace { 19 20 // Builds a SyncData for the specified |type| based on |key|. 21 syncer::SyncData BuildData(syncer::ModelType type, const std::string& key) { 22 sync_pb::EntitySpecifics specifics; 23 if (type == syncer::SYNCED_NOTIFICATIONS) { 24 specifics.mutable_synced_notification() 25 ->mutable_coalesced_notification() 26 ->set_key(key); 27 } else { 28 specifics.mutable_synced_notification_app_info() 29 ->add_synced_notification_app_info() 30 ->add_app_id(key); 31 } 32 syncer::SyncData data = syncer::SyncData::CreateLocalData( 33 key, key, specifics); 34 return data; 35 } 36 37 // Builds a SyncChange with an update to the specified |type| based on |key|. 38 syncer::SyncChange BuildChange(syncer::ModelType type, const std::string& key) { 39 syncer::SyncChangeList change_list; 40 syncer::SyncData data = BuildData(type, key); 41 return syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data); 42 } 43 44 // Verifies that the specifics within |data| match the serialized specifics 45 // within |serialized|. 46 testing::AssertionResult DataSpecificsMatch(const syncer::SyncData& data, 47 const std::string& serialized) { 48 if (data.GetDataType() == syncer::SYNCED_NOTIFICATIONS) { 49 const sync_pb::SyncedNotificationSpecifics& proto = 50 data.GetSpecifics().synced_notification(); 51 if (serialized != proto.SerializeAsString()) 52 return testing::AssertionFailure() << "Notification specifics mismatch"; 53 } else { 54 const sync_pb::SyncedNotificationAppInfoSpecifics& proto = 55 data.GetSpecifics().synced_notification_app_info(); 56 if (serialized != proto.SerializeAsString()) 57 return testing::AssertionFailure() << "App info specifics mismatch"; 58 } 59 return testing::AssertionSuccess(); 60 } 61 62 // Verifies that the update within |change| matchs the serialized specifics 63 // within |serialized|. 64 testing::AssertionResult ChangeSpecificsMatch(const syncer::SyncChange& change, 65 const std::string& serialized) { 66 if (change.change_type() != syncer::SyncChange::ACTION_UPDATE) 67 return testing::AssertionFailure() << "Invalid change type"; 68 return DataSpecificsMatch(change.sync_data(), serialized); 69 } 70 71 class SyncedNotificationsShimTest : public testing::Test { 72 public: 73 SyncedNotificationsShimTest(); 74 virtual ~SyncedNotificationsShimTest(); 75 76 // Starts sync for both sync types. 77 void StartSync(); 78 // Starts sync for the specified datatype |type|. 79 void StartSync(syncer::ModelType type); 80 81 // Transfers ownership of the last event received. 82 scoped_ptr<Event> GetLastEvent(); 83 84 SyncedNotificationsShim* shim() { return &shim_; } 85 syncer::FakeSyncChangeProcessor* notification_processor() { 86 return notification_processor_; 87 } 88 syncer::FakeSyncChangeProcessor* app_info_processor() { 89 return app_info_processor_; 90 } 91 92 private: 93 void EventCallback(scoped_ptr<Event> event); 94 95 // Shim being tested. 96 SyncedNotificationsShim shim_; 97 98 syncer::FakeSyncChangeProcessor* notification_processor_; 99 syncer::FakeSyncChangeProcessor* app_info_processor_; 100 101 // The last event fired by the shim. 102 scoped_ptr<Event> last_event_fired_; 103 }; 104 105 SyncedNotificationsShimTest::SyncedNotificationsShimTest() 106 : shim_(base::Bind(&SyncedNotificationsShimTest::EventCallback, 107 base::Unretained(this))), 108 notification_processor_(NULL), 109 app_info_processor_(NULL) {} 110 111 SyncedNotificationsShimTest::~SyncedNotificationsShimTest() {} 112 113 void SyncedNotificationsShimTest::EventCallback(scoped_ptr<Event> event) { 114 ASSERT_FALSE(last_event_fired_); 115 last_event_fired_ = event.Pass(); 116 } 117 118 scoped_ptr<Event> SyncedNotificationsShimTest::GetLastEvent() { 119 return last_event_fired_.Pass(); 120 } 121 122 void SyncedNotificationsShimTest::StartSync() { 123 StartSync(syncer::SYNCED_NOTIFICATIONS); 124 StartSync(syncer::SYNCED_NOTIFICATION_APP_INFO); 125 GetLastEvent(); 126 } 127 128 void SyncedNotificationsShimTest::StartSync(syncer::ModelType type) { 129 scoped_ptr<syncer::FakeSyncChangeProcessor> change_processor( 130 new syncer::FakeSyncChangeProcessor()); 131 if (type == syncer::SYNCED_NOTIFICATIONS) 132 notification_processor_ = change_processor.get(); 133 else 134 app_info_processor_ = change_processor.get(); 135 syncer::SyncDataList sync_data; 136 shim_.MergeDataAndStartSyncing( 137 type, 138 sync_data, 139 change_processor.PassAs<syncer::SyncChangeProcessor>(), 140 scoped_ptr<syncer::SyncErrorFactory>()); 141 } 142 143 // Starting sync should fire the sync started event, but only after both types 144 // have started. 145 TEST_F(SyncedNotificationsShimTest, StartSync) { 146 EXPECT_FALSE(shim()->IsSyncReady()); 147 StartSync(syncer::SYNCED_NOTIFICATIONS); 148 EXPECT_FALSE(shim()->IsSyncReady()); 149 EXPECT_FALSE(GetLastEvent()); 150 151 StartSync(syncer::SYNCED_NOTIFICATION_APP_INFO); 152 EXPECT_TRUE(shim()->IsSyncReady()); 153 154 scoped_ptr<Event> event = GetLastEvent(); 155 ASSERT_TRUE(event); 156 EXPECT_EQ(synced_notifications_private::OnSyncStartup::kEventName, 157 event->event_name); 158 159 EXPECT_TRUE(notification_processor()->changes().empty()); 160 EXPECT_TRUE(app_info_processor()->changes().empty()); 161 } 162 163 // A sync update should fire the OnDataChanges event with the updated 164 // notification. 165 TEST_F(SyncedNotificationsShimTest, ProcessSyncChangesSingleNotification) { 166 StartSync(); 167 syncer::SyncChangeList change_list; 168 change_list.push_back(BuildChange(syncer::SYNCED_NOTIFICATIONS, "key")); 169 shim()->ProcessSyncChanges(FROM_HERE, change_list); 170 scoped_ptr<Event> event = GetLastEvent(); 171 ASSERT_TRUE(event); 172 EXPECT_EQ(synced_notifications_private::OnDataChanges::kEventName, 173 event->event_name); 174 ASSERT_TRUE(event->event_args); 175 EXPECT_EQ(1U, event->event_args->GetSize()); 176 177 base::ListValue* args = NULL; 178 ASSERT_TRUE(event->event_args->GetList(0, &args)); 179 EXPECT_EQ(1U, args->GetSize()); 180 base::DictionaryValue* sync_change_value = NULL; 181 ASSERT_TRUE(args->GetDictionary(0, &sync_change_value)); 182 scoped_ptr<synced_notifications_private::SyncChange> sync_change = 183 synced_notifications_private::SyncChange::FromValue(*sync_change_value); 184 ASSERT_TRUE(sync_change); 185 EXPECT_TRUE(ChangeSpecificsMatch(change_list[0], 186 sync_change->data.data_item)); 187 EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED, 188 sync_change->change_type); 189 } 190 191 // Verify that multiple notification updates can be sent in one event. 192 TEST_F(SyncedNotificationsShimTest, ProcessSyncChangesMultipleNotification) { 193 StartSync(); 194 syncer::SyncChangeList change_list; 195 change_list.push_back(BuildChange(syncer::SYNCED_NOTIFICATIONS, "key")); 196 change_list.push_back(BuildChange(syncer::SYNCED_NOTIFICATIONS, "key2")); 197 shim()->ProcessSyncChanges(FROM_HERE, change_list); 198 scoped_ptr<Event> event = GetLastEvent(); 199 ASSERT_TRUE(event); 200 EXPECT_EQ(synced_notifications_private::OnDataChanges::kEventName, 201 event->event_name); 202 ASSERT_TRUE(event->event_args); 203 base::ListValue* args = NULL; 204 ASSERT_TRUE(event->event_args->GetList(0, &args)); 205 EXPECT_EQ(2U, args->GetSize()); 206 207 base::DictionaryValue* sync_change_value = NULL; 208 ASSERT_TRUE(args->GetDictionary(0, &sync_change_value)); 209 scoped_ptr<synced_notifications_private::SyncChange> sync_change = 210 synced_notifications_private::SyncChange::FromValue(*sync_change_value); 211 ASSERT_TRUE(sync_change); 212 EXPECT_TRUE(ChangeSpecificsMatch(change_list[0], 213 sync_change->data.data_item)); 214 EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED, 215 sync_change->change_type); 216 217 ASSERT_TRUE(args->GetDictionary(1, &sync_change_value)); 218 sync_change = 219 synced_notifications_private::SyncChange::FromValue(*sync_change_value); 220 ASSERT_TRUE(sync_change); 221 EXPECT_TRUE(ChangeSpecificsMatch(change_list[1], 222 sync_change->data.data_item)); 223 EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED, 224 sync_change->change_type); 225 } 226 227 // Verify AppInfo updates trigger OnDataChanges events. 228 TEST_F(SyncedNotificationsShimTest, ProcessSyncChangeAppInfo) { 229 StartSync(); 230 syncer::SyncChangeList change_list; 231 change_list.push_back( 232 BuildChange(syncer::SYNCED_NOTIFICATION_APP_INFO, "key")); 233 shim()->ProcessSyncChanges(FROM_HERE, change_list); 234 scoped_ptr<Event> event = GetLastEvent(); 235 ASSERT_TRUE(event); 236 EXPECT_EQ(synced_notifications_private::OnDataChanges::kEventName, 237 event->event_name); 238 ASSERT_TRUE(event->event_args); 239 EXPECT_EQ(1U, event->event_args->GetSize()); 240 241 base::ListValue* args = NULL; 242 ASSERT_TRUE(event->event_args->GetList(0, &args)); 243 EXPECT_EQ(1U, args->GetSize()); 244 base::DictionaryValue* sync_change_value = NULL; 245 ASSERT_TRUE(args->GetDictionary(0, &sync_change_value)); 246 scoped_ptr<synced_notifications_private::SyncChange> sync_change = 247 synced_notifications_private::SyncChange::FromValue(*sync_change_value); 248 ASSERT_TRUE(sync_change); 249 EXPECT_TRUE(ChangeSpecificsMatch(change_list[0], 250 sync_change->data.data_item)); 251 EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED, 252 sync_change->change_type); 253 } 254 255 // Attempt to get the initial sync data both before and after sync starts. 256 TEST_F(SyncedNotificationsShimTest, GetInitialData) { 257 std::vector<linked_ptr<synced_notifications_private::SyncData> > data; 258 EXPECT_FALSE(shim()->GetInitialData( 259 synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION, &data)); 260 EXPECT_FALSE(shim()->GetInitialData( 261 synced_notifications_private::SYNC_DATA_TYPE_APP_INFO, &data)); 262 263 StartSync(); 264 265 EXPECT_TRUE(shim()->GetInitialData( 266 synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION, &data)); 267 EXPECT_TRUE(data.empty()); 268 EXPECT_TRUE(shim()->GetInitialData( 269 synced_notifications_private::SYNC_DATA_TYPE_APP_INFO, &data)); 270 EXPECT_TRUE(data.empty()); 271 272 notification_processor()->data().push_back(BuildData( 273 syncer::SYNCED_NOTIFICATIONS, "notif_key")); 274 EXPECT_TRUE(shim()->GetInitialData( 275 synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION, &data)); 276 EXPECT_EQ(1U, data.size()); 277 EXPECT_TRUE(DataSpecificsMatch(notification_processor()->data()[0], 278 data[0]->data_item)); 279 280 data.clear(); 281 app_info_processor()->data().push_back(BuildData( 282 syncer::SYNCED_NOTIFICATION_APP_INFO, "app_key")); 283 EXPECT_TRUE(shim()->GetInitialData( 284 synced_notifications_private::SYNC_DATA_TYPE_APP_INFO, &data)); 285 EXPECT_EQ(1U, data.size()); 286 EXPECT_TRUE(DataSpecificsMatch(app_info_processor()->data()[0], 287 data[0]->data_item)); 288 } 289 290 // Verify that notification updates are properly handled. 291 TEST_F(SyncedNotificationsShimTest, UpdateNotification) { 292 syncer::SyncData data = 293 BuildData(syncer::SYNCED_NOTIFICATIONS, "notif_key"); 294 std::string serialized = 295 data.GetSpecifics().synced_notification().SerializeAsString(); 296 EXPECT_FALSE(shim()->UpdateNotification(serialized)); 297 298 StartSync(); 299 300 EXPECT_FALSE(shim()->UpdateNotification("gibberish")); 301 EXPECT_TRUE(notification_processor()->changes().empty()); 302 303 EXPECT_TRUE(shim()->UpdateNotification(serialized)); 304 EXPECT_EQ(1U, notification_processor()->changes().size()); 305 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, 306 notification_processor()->changes()[0].change_type()); 307 EXPECT_EQ(syncer::SYNCED_NOTIFICATIONS, 308 notification_processor()->changes()[0].sync_data().GetDataType()); 309 EXPECT_EQ(serialized, 310 notification_processor() 311 ->changes()[0] 312 .sync_data() 313 .GetSpecifics() 314 .synced_notification() 315 .SerializeAsString()); 316 } 317 318 // Verify that SetRenderContext updates the datatype context properly. 319 TEST_F(SyncedNotificationsShimTest, SetRenderContext) { 320 const std::string kContext = "context"; 321 EXPECT_FALSE(shim()->SetRenderContext( 322 synced_notifications_private::REFRESH_REQUEST_REFRESH_NEEDED, kContext)); 323 324 StartSync(); 325 326 EXPECT_TRUE(shim()->SetRenderContext( 327 synced_notifications_private::REFRESH_REQUEST_REFRESH_NEEDED, kContext)); 328 EXPECT_EQ(kContext, notification_processor()->context()); 329 } 330 331 } // namespace 332